4

I am trying to make a simple Python script that enters a given password in the command line after using the 'su' command (or any other command which requires administrator privileges or simply requires a password in order to be executed).

I tried to use the Subprocess module for this as well as pynput, but haven't been able to figure it out.

import subprocess
import os

# os.system('su') # Tried using this too

process = subprocess.Popen('su', stdin=subprocess.PIPE, stdout=subprocess.PIPE)
process.stdin.write(b"password_to_enter")
print(process.communicate()[0])
process.stdin.close()

I was expecting this to enter 'password_to_enter' in the given password prompt after typing the 'su' command, but it didn't. I tried giving it the correct password as well but still did not work.

What am I doing wrong?

PS: I am on Mac

FedeCuci
  • 117
  • 1
  • 1
  • 8

1 Answers1

6

The su command expects to be reading from a terminal. Running your example above on my Linux machine returns the following error:

su: must be run from a terminal

This is because su tries to make sure it's being run from a terminal. You can bypass this by allocating a pty and managing input and output yourself, but getting this right can be pretty tricky because you can't enter the password until after su prompts for it. For example:

import subprocess
import os
import pty
import time

# Allocate the pty to talk to su with.
master, slave = pty.openpty()

# Open the process, pass in the slave pty as stdin.
process = subprocess.Popen('su', stdin=slave, stdout=subprocess.PIPE, shell=True)

# Make sure we wait for the "Password:" prompt.
# The correct way to do this is to read from stdout and wait until the message is printed.
time.sleep(2)

# Open a write handle to the master end of the pty to write to.
pin = os.fdopen(master, "w")
pin.write("password_to_enter\n")
pin.flush()

# Clean up
print(process.communicate()[0])
pin.close()
os.close(slave)

There's a library called pexpect that makes interacting with interactive applications pretty simple:

import pexpect
import sys

child = pexpect.spawn("su")
child.logfile_read = sys.stdout
child.expect("Password:")
child.sendline("your-password-here")
child.expect("#")
child.sendline("whoami")
child.expect("#")
Michael Powers
  • 1,970
  • 1
  • 7
  • 12
  • Thanks. Question: How secure is this against man in middle attacks? E.g. can another non-root process hijack this PTY communication by reading from it before the `su` does? – caveman Aug 12 '20 at 05:12
  • Another question: your code above does not capture "Password:" prompt by `su`. I can still see it in my terminal. Any idea why is this so? – caveman Aug 12 '20 at 05:23
  • 1
    Generally TTY pipes are pretty safe against man in the middle attacks. On Linux as root it's possible to clone and intercept the pipes from different processes but you should be safe against normal users. The password prompt is not captured because of the `stdout=PIPE` bit, if you switch that to DEVNULL it should hide the output (see the [subprocess](https://docs.python.org/3/library/subprocess.html) documentation). – Michael Powers Aug 12 '20 at 13:23
  • Thanks thanks. What about using named pipes? The app that I can feed it sensitive data via TTY, also allows me to feed it via a file. So I can use named pipes too. Any thoughts on the security of named pipes compared to TTY? – caveman Aug 12 '20 at 17:57
  • Other questions please: how safe is `os.close(slave)`? Why not `pty.os.close(slave)`? And why didn't you close the master? – caveman Aug 12 '20 at 20:57