"history -c" doesn't work when called inside a script? - windows

I wrote a bash script to call a Python script that encrypts private data using AES, taking a filepath and a 256-bit password as the only arguments. After encryption is done, it clears the history so the password isn't sitting there in case I leave the terminal open. It looks something like this:
#!/bin/bash
python aesencrypt.py "$1" "$2"
history -c
echo "" > ~/.bash_history
The ~/.bash_history file is cleared just fine, but if I run history after running this script then all of my history is still there (until I exit the terminal). Is there anything I'm missing here?

Don't try to clear history -- even though that's the most obvious way that passing a password on the command line exposes it, that action is giving a false sense of security: Passwords given on the command line are trivial to capture via other processes running on the same machine (even under untrusted accounts!) even without history involved at all.
Moreover, as you note, a shell can only modify its own in-memory state, not the in-memory state of the separate process that started it (which may not even be the same shell, or a shell at all!).
Instead, modify your Python program's calling convention to read the password direct from the TTY (as SSH does), or from the environment. For the latter, usage might look like:
# assumes you renamed aesencrypt.py to aesencrypt, ran chmod +x, and gave a valid shebang
password="somePassword" aesencrypt outFile
...and you would want to modify your Python script to do something like:
#!/usr/bin/env python
import os, sys
filename = sys.argv[1]
password = os.environ['password']
# ...put the rest of your logic here.

Related

Run multiple commands in a non-interactive shell session and parse the output

I would like to communicate with a (remote) non-interactive shell via its stdin/stdout to run multiple commands and read the outputs. The problem is that if I stuff multiple commands on shell stdin, I am not able to detect the boundaries between outputs of individual commands.
In Python-like pseudo-code:
sh = Popen(['ssh', 'user#remote', '/bin/bash'], stdin=PIPE, stdout=PIPE)
sh.stdin.write('ls /\n')
sh.stdin.write('ls /usr\n')
sh.stdin.close()
out = sh.stdout.read()
But obviously out contains the outputs of both commands concatenated, and I have no way of reliably splitting them.
So far my best idea is to insert \0 bytes between the outputs:
sh.stdin.write('ls /; echo -ne "\0"\n')
sh.stdin.write('ls /usr; echo -ne "\0"\n')
Then I can split out on zero characters.
Other approaches that don't work for me:
I don't want to run a separate ssh session per command, as the handshake is too heavyweight.
I'd prefer not to force ControlMaster options on the created shells to respect end-user's ssh_config.
I'd prefer to not need require users to install specific server programs.
Is there a better way of running several commands in one session and getting individual outputs? Is there a widely-deployed shell with some sort of binary output mode?
PS. There is a duplicate question, but it doesn't have a satisfactory answer:
Run multiple commands in a single ssh session using popen and save the output in separate files
For SSH I used paramiko and its invoke_shell method to create a programmatically-manageable shell instance.
The following is not a complete answer, it's still hacky, but I feel it's a step in the right direction.
I required the same read/write shell instance functionality in Windows but have had no luck, so I extended your approach a little (thank you for the idea by the way).
I verify each command executes successfully based on its exit code by placing a conditional exit between each command, then I use the text of said conditional check (a known string) as the delimiter to define each command's response.
A crude example:
from subprocess import Popen, PIPE
sh = Popen('cmd', stdin=PIPE, stdout=PIPE)
sh.stdin.write(b'F:\r\n')
sh.stdin.write(b"if not %errorlevel% == 0 exit\r\n")
sh.stdin.write(b'cd F:\\NewFolder\r\n')
sh.stdin.write(b"if not %errorlevel% == 0 exit\r\n")
sh.stdin.write('...some useful command with the current directory confirmed as set to F:\NewFolder...')
sh.stdin.close()
out = sh.stdout.read()
sh.stdout.close()
# Split 'out' by each line that ends with 'if not %errorlevel% == 0 exit' and do what you require with the responses

Bash script calls vi for manual editing, then script resumes?

I wrote a script that creates a backup of a text file, and a second script that verifies some syntax in text file using SED.
In the middle, there is a manual process: Users edit the original file adding some strings. This process must remain manual.
I would like to merge my two scripts so the backup is created, vi is open for the user, when the user is done editing the file, the script resumes doing the syntax verification.
I am learning by doing, but really do not know how to code the "open vi, wait for the user to do his editing, take control over and resume with verification" part.
I read there is a function called system (in Perl) that could be used, but my code is in BASH.
Any suggestions on how to get this done in BASH? Thanks!
In bash, each statement is essentially like an implicit call to system (unless it's a builtin shell command) since shell scripts are designed to make it easy to run other programs.
backup some_file.txt
vi some_file.txt # The script blocks until the user exits vi
verify_syntax some_file.txt
The only difference between using vi and a command like ls is that ls will do its thing and exit without user intervention, while vi (or any interactive command) will run until the user explicitly exits.

How do I send commands to the ADB shell directly from my app?

I want to send commands in the ADB shell itself as if i had done the following in cmd.
>adb shell
shell#:/ <command>
I am using python 3.4 on a windows 7 OS 64bit machine. I can send one-line shell commands simply using subprocess.getoutput such as:
subprocess.getoutput ('adb pull /storage/sdcard0/file.txt')
as long as the adb commands themselves are recognized by ADB specifically, such as pull and push, however there are other commands such as grep that need to be run IN the shell, like above, since they are not recognized by adb. for example, the following line will not work:
subprocess.getoutput ('adb shell ls -l | grep ...')
To enter the commands in the shell I thought I needed some kind of expect library as that is what 'everyone' suggests, however pexpect, wexpect, and winexpect all failed to work. they were written for python 2 and after being ported to python 3 and my going through the .py files by hand, even those tweaked for windows, nothing was working - each of them for different reasons.
how can i send the input i want to the adb shell directly?
If none of the already recommended shortcuts work for you you can still go the 'regular' way using 'subprocess.Popen' for entering commands in the adb shell with Popen:
cmd1 = 'adb shell'
cmd2 = 'ls -l | grep ...'
p = subprocess.Popen(cmd1.split(), stdin=PIPE)
time.sleep(1)
p.stdin.write(cmd2.encode('utf-8'))
p.stdin.write('\n'.encode('utf-8'))
p.stdin.flush()
time.sleep(3)
p.kill()
Some things to remember:
even though you import subprocess you still need to invoke subprocess.Popen
sending cmd1 as a string or as items in a list should work too but '.split()' does the trick and is easier on the eyes
since you only specidfied you want to enter input to the shell you only need stdin=PIPE. stdout would only be necessary if you wanted to receive output from the shell
time.sleep(1) isn't really necessary, however since many complained about input issues being faster or slower in python 2 vs 3 consider maybe using it. 'they' might have been using versions of 'expect' that need the shell's reply first. this code also worked when i tested it with simply swapping out and in the process with time.sleep(0)
stdin.write will return an error if the input is not encoded properly. python's default is unicode. entering by binary did not work for me in my tests like this "b\ls ..." but .encode() worked. dont forget the endline!
if you use .encode() there is a worry that the line might not get sent properly, so to be sure it might be good to include a flush().
time.sleep(3) is completely uneccesary, but if your command takes a long time to execute (eg a regressive search through the entire device piped out to a txt file on the memory card) maybe give it some extra time before killing anyhting.
remember to kill. if you didnt kill it, the pipe may remain open, and even after exiting the test app on the console the next commend still went to the shell even though the prompt appearsed to be my regular cmd prompt.
Amichai, I have to start with pointing out that your own "solution" is pretty awful. And your explanation makes it even worse. Doing all those unnecessary things just because you do not understand how shell (here I mean your PC's OS shell, not adb) command parsing works.
When all you needed was just this one command:
subprocess.check_output(['adb', 'shell', 'ls /storage/sdcard0 | grep ...']).decode('utf-8')

Automatically decrypt and run an encrypted bash script without saving decrypted file to file system

I have a shell script that produces sensitive content when run. It sits on a box that only a few users have permissions to access. However, I have also added layered obfuscation to prevent unauthorized usage, via the following:
script must be run as root
script must be passed specific command line arguments to produce any output
script has been encoded by the shell compiler "shc" to mask facts #1 and #2 from normal users (those who would not know to use TRACE or STRINGS to still view the actual code).
To then add a layer of actual security to protect again more advanced users and system admins, I have also encrypted the script with gpg.
My question is -- Is there a gpg command (or other encryption method) that I could run which prompts for the decryption passphrase, and decrypts the script and runs it in memory only (without saving the decrypted version of the file to the file system)?
I realize that sensitive information may still exist in unprotected memory while being executed, I'll address that separately.
You can capture the output of decrypting by
decrypted=$(gpg -d ...)
You can then eval the result
eval "$decrypted"
Another simple option to contrast with choroba's answer:
Save the decrypted output to a file in /dev/shm/. (It's an in-ram tmpfs filesystem there by default on virtually all Linux distros.) Setup a trap to delete the file when your script exits.
It's very possible I could refine this, but here's another idea where you execute the script rather than evaluate it like in choroba's example. It allows you to pass arguments...
bash <( gpg -d ... ) arg1 arg2
...it 'overrides' the interpreter, though. I.e. I'd run my scripts with bash -ue. May or may not be a problem depending on the scripts and whether you are writing them yourself or not :)

Get Input for a bash script by capturing it from a VIM session

I am creating a new CLI application, where I want to get some sensitive input from the user. Since, this input can be quite descriptive as well as the information is a bit sensitive, I wanted to allow user to enter a command like this from this app:
app new entry
after which, I want to provide user with a VIM session where he can write this descriptive input, which when he exits from this VIM session, will be captured by my script and used for further processing.
Can someone tell me a way (probably some hidden VIM feature - since, I am always amazed by them) so that I can do so, without creating any temporary file? As explained in a comment below, I would prefer a some-what in-memory file, since the information can be a bit sensitive, and hence, I would like to process it first via my script and then only, write it to disk in an encrypted manner.
Git actually does this: when you type git commit, a new Vim instance is created and a temporary file is used in this instance. In that file, you type your commit message
Once Vim gets closed again, the content of the temporary file is read and used by Git. Afterwards, the temporary file gets deleted again.
So, to get what you want, you need the following steps:
create a unique temporary file (Create a tempfile without opening it in Ruby)
open Vim on that file (Ruby, Difference between exec, system and %x() or Backticks)
wait until Vim gets terminated again (also contained in the above SO thread)
read the tempoarary file (How can I read a file with Ruby?)
delete the temporary file (Deleting files in ruby)
That's it.
You can make shell create file descriptors attached to your function and make vim write there, like this: (but you need to split script in two parts: one that calls vim and one that processes its input):
# First script
…
vim --cmd $'aug ScriptForbidReading\nau BufReadCmd /proc/self/fd/* :' --cmd 'aug END' >(second-script)
. Notes:
second-script might actually be a function defined in first script (at least in zsh). This also requires bash or zsh (tested only on the latter).
Requires *nix, maybe won’t work on some OSes considered to be *nix.
BufReadCmd is needed because vim hangs when trying to read write-only descriptor.
It is suggested that you set filetype (if needed) right away, without using ftdetect plugins: in case your script is not the only one which will use this method.
Zsh will wait for second-script to finish, so you may continue script right after vim command in case information from second-script is not needed (it would be hard to get from there).
Second script will be launched from a subshell. Thus no variable modifications will be seen in code running after vim call.
Second script will receive whatever vim saves on standard input. Parent standard input is not directly accessible, but using </dev/tty will probably work.
This is for zsh/bash script. Nothing will really prevent you from using the same idea in ruby (it is likely more convenient and does not require splitting into two scripts), but I do not know ruby enough to say how one can create file descriptors in ruby.
Using vim for this seems like overkill.
The highline ruby gem might do what you need:
require 'highline'
irb> pw = HighLine.new.ask('info: ') {|q| q.echo = false }
info:
=> "abc"
The user's text is not displayed when you set echo to false.
This is also safer than creating a file and then deleting it, because then you'd have to ensure that the delete was secure (overwriting the file several times with random data so it can't be recovered; see the shred or srm utilities).

Resources