I'm trying to add users to the server running my node application. Right now I am using:
exec("useradd -mp {password-hash}")
with a hash that gets created by
exec("mkpasswd -m des {password}")
This leaves the password visible in the process list, and that should be avoided at all costs.
That's why I am wondering if there is a module on npm or an easier way to add a linux user to the system programmatically (this is necessary because the app I am building creates a user account on the system when a user registers on the web). Can this be done without evoking system commands like I am currently doing? I have found neither npm module nor any information of using node to interact with ubuntu/unix user management.
Solution
Using Mustafa's hints from the top answer, I implemented a version of the mkpasswd command using spawn. In CoffeeScript, it would look like this:
{spawn} = require 'child_process'
child = spawn 'mkpasswd', ['-m','des','--stdin']
child.stdout.on 'data', (data)->
console.log 'password hash arrived: ', data.toString()
child.stdin.write 'password'
Make sure you add the proper error handling event handlers to child.stderr and child.stdout while debugging your code.
You either use those commands or manually add your user to /etc/passwd and its password to /etc/shadow.
Format of a /etc/passwd entry: username:passwd:UID:GID:full_name:directory:shell
Passwd should be x if you want it o make it secure, so it will read from shadow.
Format of a /etc/shadow entry: username:passwd:last:may:must:warn:expire:disable:reserved
If you add them correctly, there will be no problem.
But why would you bother with it manually while you have the required tool? If you are concerned about the privacy, you can issue mkpasswd -m des --stdin which will read the password from standart input. Instead of exec, when you use spawn, you can also control the stdin and stdout of the processes. stdin is just a writable stream, you can write to it and read the output from stdout. Or you can find a npm module that generates the hash with given algorithms, DES, AES etc.
Related
I started implementing tkinter front end to Linux /usr/bin/pass (will call it PASS for readability) utility (/usr/bin/pass, http://www.passwordstore.org/⟩. Objective: learn Python, tkinter, also I may get secure password manager.
The task didn't look hard at first. Then I encountered two problems which are also my questions.
"pass edit pass-name" command calls vim, and if the file has changed gpg encrypts it then saves in the ~/.pasword-store dir. Ideally I'd like to ask a user for a new password (reserved a tk.Entry for that) and send the new password to PASS.
Is there a way to send a new password to PASS programmatically (without manually editing a file in vim)?
[PASS also makes git commits]
PASS commands may ask for passphrase, e.g., "pass show pass-name". As I understand it is done by gpg-agent (in the terminal where I launch Python appears a question "Enter passphrase: "). I'd like to ask for passphrase in the tkinter app and then send the passphrase to PASS [, gpg, or gpg-agent?]
Is it possible?
Using:
~/.gnupg/gpg.conf
use-agent
pinentry-mode loopback
~/.gnupg/gpg-agent.conf
allow-loopback-pinentry
default-cache-ttl
max-cache-ttl
Python 3.9.2
/usr/bin/pass v1.7.3
gpg (GnuPG) 2.2.27
Debian GNU/Linux 11 (bullseye)
UPDATE 1/18/23
Regarding providing a passphrase to PASS. Going to investigate the following approach: suppress gpg-agent from asking a passphrase but return an error instead 2) catch this error in Python 3) force gpg-agent to cache the passpharse 4) repeat the failed action.
I have a unix command 'abc' which gives me an output
This abc lies on my server.
But when i run this command from server, i want to restrict the output of it to be seen by people.
By the above statement , i meant..
For eg. If i say:
ls dirname
I can see the output of the above command on the console.
So, if the command is run from command-line, i dont want to have echoed on the console. I cant use /dev/null as I am using the same command from my program where I need the output to be assigned to a variable and then use it further in my application.
However, I want to get the output of this command when I call this from my program.
How can I differentiate the call in this regard.
The command whoami gives you the current logged user, and the command last -i outputs information of the last logged users in the system, including the IP address (3rd column) and the timestamp or a string stating that the user is stil logged in.
With that in mind you could pipe these commands:
last -i | grep $(whoami) | grep 'still logged in'
which will provide an output like this:
(username) pts/2 0.0.0.0 Wed Dec 23 18:58 still logged in
(username) :0 0.0.0.0 Wed Dec 23 11:13 still logged in
so if you are running a shell in the same host, the IP will be 0.0.0.0 and different otherwise. You can extract the IP string by piping awk at the end of the command.
However, addhering to the philosophy in unix systems of Do One Thing and Do It Well, I'd suggest a different approach, split your command into 2 different commands:
A command to be used by the clients, where the output is whatever you
want the clients to see
Another command (offering 2 options, since there isnt much detail in the question):
Either extending the first command, adding the additional output, and using this one from your application
Or just generating the additional output, and using a combination of the 2 commands from your application
Some of the benefits you can get by following this approach:
Performing checks to verify where the command was issued from, is no longer necessary
Avoid coupling issues
Easier to maintain
Updated: added the means to extract the IP of the current user at the beggining of the answer.
You were a little vague on the complete setup, so I'll have to infer a few things. Since you mentioned, "my" server, I assume you can set permissions on files, change ownership on files, etc (e.g. you can become root).
I also have to infer that the target abc program just produces some output and doesn't need to modify any files to speak of [other than (e.g.) /tmp/temp.$$]
As an example, let's do this from your home directory. Move the program abc to $HOME/private_bin and set the directory permission to 700 which means that only you can execute it.
Create a second directory: $HOME/public_bin that has normal permissions. Create a "launcher" program [let's call it abcpub] and put it in this directory. Set the permissions of abcpub to 4741. It's now a setuid program. Note that any non-root user may do this for files they own. It is not like creating a sudo because an ordinary user would need to do chown root ...
Now we're set ...
You can access the real abc program anytime you want. Others have no direct access to abc.
The launcher abcpub will allow others to have access to abc, but the launcher can apply whatever restrictions you desire: including no access, output to /dev/null, etc. abcpub can look at getuid and geteuid to determine who is executing it [you or somebody else]
We did the above example using your own uid and home directory. But, we can repeat the process by creating an "abc" user in /etc/passwd and a /home/abc. The abc user could be set up with a shell of /sbin/nologin. Thus, it's similar to nobody and it can't hurt anything.
It may be even better doing this by creating a setgrp program instead of setuid as that allows better comingling. The original user could retain their user permissions but still get access via the new group.
Also, it may be possible to configure sudo to get what you want.
Not sure if this is the right place to ask.
Say I write a shell that takes stdin input, filters this input so let's say only certain commands like
ls (list contents of binary directory and subdirectory)
update (git clone)
build (go build)
test (go test)
start (systemctl start this.service only)
stop (systemctl stop this.service only)
running (is the binary being executed and with how many GOMAXPROCS?)
usage (memory, cpu usage)
gensvc (generate .service file)
exit (leave shell/logout)
work, you guessed it, I'm trying to give a user only very limited maintenance access over ssh.
Say I'm careful with \0 (I'd write it in Go anyway using bufio.Scanner)
Is there any way to stop the running shell and execute /bin/sh or similar or any way to get around this shell?
The idea is a user should push their stuff via git to a bare repo, this repo is cloned to the filesystem to a certain directory, then go build is called and the binary is ran with a systemd .service file that is generated previously.
Thinking logically, if the user is only able to write certain strings that are accepted, no there is no way. But maybe you know of one, some ctrl+z witchcraft ;) or whatever.
The only attack surface is the input string or rather bytes. Of course the user could git push a program that builds its own shell or runs certain commands, but that's out of scope (I would remove capabilities with systemd and restrict device access and forbid anything but the connection to the database server, private tmp and all, namespace and subnamespace it TODO)
The only problem I see is git pushing but I'm sure I could work around that in a git only mode argv and adding it to ~/.ssh/authorized_keys. something like lish gitmode and execute stdin commands if they start with git or something like it.
Example:
https://gist.github.com/dalu/ce2ef43a2ef5c390a819
If you're only allowed certain commands, your "shell" will read the command, parse it and then execute it then you should be fine, unless I misunderstood it.
Go "memory" can't be executed, not without you doing some nasty hacks with assembly anyway, so you don't have to worry about shell injection.
Something along these lines should be safe:
func getAction() (name string, args []string) {
// read stdin to get the command of the user
}
func doAction() {
for {
action, args := getAction()
switch action {
case "update": //let's assume the full command is: update https://repo/path.git
if len(args) != 1 {
//error
}
out, err := exec.Command("/usr/bin/git", "clone", "--recursive", args[0]).CombinedOutput()
// do stuff with out and err
}
}
}
If you are implementing the shell yourself and directly executing the commands via exec() or implementing them internally, then it is certainly possible to produce a secure restricted shell. If you are just superficially checking a command line before passing it on to a real shell then there will probably be edge cases you might not expect.
With that said, I'd be a bit concerned about the test command you've listed. Is it intended to run the test suite of a Go package the user uploads? If so, I wouldn't even try to exploit the restricted shell if I was an attacker: I'd simply upload a package with tests that perform the actions I want. The same could be said for build/start.
Have it reviewed by a pentesting team.
People can be very creative when breaking out a sandbox of any type. Only if you never accept the user's input you can consider yourself rather safe on premises (but here any command is an input) - paper security assumptions are considered a weak to assess the software. They are similar to 'no-bug' assumptions for an algorithm on paper: as soon as you implement it, 99% of time a bug raises
In Node, I'm using a module (GM) and noticed that it uses spawn from the child_process module to pass arguments to GraphicMagick's convert executable.
I'm passing user-submitted information to GM. Is there a security concern that the user could do some sort of injection attack using a pipe (or other command line trickery)? Or does spawn protect against that? If not, is there a best practice for escaping user submitted values in this case?
We recently published a blog post on avoiding command injection vulnerabilities in node.js. It explains a bit about how spawn prevents this.
If gm was using child_process.exec there would be a greater chance of injection. This is because child_process.exec executes the commands under a subshell and not directly, letting shell meta characters like backticks, $(), ;, &&, || etc to be used nefariously.
The resulting system call looks like this with .exec() for a simple ls -l that might take user input.
[pid 25170] execve("/bin/sh", ["/bin/sh", "-c", "ls -l user input"],
[/* 16 vars */]
Since gm uses spawn the resulting system call would look something like this.
[pid 25565] execve("/bin/ls", ["/bin/ls", "-l", "."], [/* 16 vars */]
As gm would be the first argument to execve. This means that a user cannot run subcommands in the shell using pipes and other command line trickery, because in our example /bin/ls has no idea what to do with backticks or pipes or ;. It’s /bin/bash that is going to be interpreting those commands. It’s similar to using parametrized vs string-based SQL queries, if you are familiar with that.
This does however come with a caveat: using spawn is not always a safe thing. User provided arguments could still potentially have a bad outcome, maybe not command injection but something else. Check with the behavior of gm and the arguments that you are pass in user provided input into and think about how the user might be able to abuse that argument.
So, here’s the generic collective guidance for running system commands from node.js:
Avoid using child_process.exec, and never use it if the command contains any input that changes based on user input.
Try to avoid letting users pass in options to commands if possible. Typically values are okay when using spawn or execfile, but selecting options via a user controlled string is a bad idea.
If you must allow for user controlled options, look at the options for the command extensively, determine which options are safe, and whitelist only those options.
I've created a new whoami command which requires a fake username and have put it in the PATH by adding it to ~/.profile . It is created in a way that whoami is called before actual the actual whoami from Linux.
The main reason to do this is because I am remote accessing a Hadoop cluster and want the copied files to be under the fake username.
This works fine when I call whoami in the shell and even calling $PATH shows the path to my created whoami before everything else. But for some reason, when Hadoop is called, it doesn't pick the created `whoami'.
Can someone help me with how to fix this?
thanks
Most applications do not use whoami to determine a user's username or group. For instance, in bash you can use the command id to find more detailed information about yourself or id [username] (such as id root) to find out more detailed information about other users. Groups can be found with groups as well. Also, different programming languages, such as C, have their own methods of determining user identities such as the getuid() command.
If you really "need" to go as far as faking your user account, you'll need to go down to OS level and create hooks into the kernel/API that handles those methods.
Is it possible that you simply chown the files after they are copied instead?
UPDATE:
It appears that some releases of Hadoop do actually use whoami (my own implementation w/ clustering does not).
In this event, the best (a term loosely used) suggestion would be to move the legitimate whoami executable and create a whoami shell script that goes in it's place. The custom script should validate the current user and if it's "hadoop", return whatever faked username you want - otherwise return valid output. Igor's answer would work in this case.
I suppose that hadoop uses other PATH variable then you have in your shell.
You can tune its PATH and add the directory with fake whoami to its beginning.
When it is impossible,
you can write a small wrapper for whoami (I'm not sure that it is a good idea but you can do this if you want) that will run original whoami except when the script is executed by hadoop:
#!/bin/sh
WHOAMI=/bin/whoami.orig
if [ "$($WHOAMI)" = hadoop ]
then
echo fake
else
exec $WHOAMI "$#"
fi