How to simulate stdin in shell script and check the stdout answer - linux

I'm currently doing the overthewire exercises and I'm stuck at one point, or more precisely I would like to solve a lvl doing differently from what I could find on the web.
I have to pass a password to a nc command with a pin code, and according to that I have an answer written on stdout (telling if it's right or wrong).
The pass is made with a fix first part, and a pin code between 0000 and 9999.
The fact is that by doing this with cmd line, it's like : echo "my pass" "4-digits pin code" | nc localhost 30002
Then I have the answer depending whether my pin is correct or not.
What I would like to do as a script (maybe I'm totally wrong since this very first step) :
I check with the man that nc could accept to launch a script with the '-e' option.
So I decided to build a script to do the following :
I call the localhost on the port : nc localhost 30002 -e "myScript"
Within my script, I would like to, recursively, echo the combination of the pass and a 4 digit pin to the stdin, in order for me to make the server answering right or wrong.
Then I check the stdout (where I suppose I can read the right or wrong answer from the server), to define if I find the right pincode.
Just to say, I only know the wrong answer that I can have, no idea what the right answer can be.
Here is the code I tried to build (don't flame plz, I'm new :) ):
#!/bin/bash
pwd=UoMYTrfrBFHyQXmg6gzctqAwOmw1IohZ
#nc localhost 30002
for i in {0001..9999}
do
cat <<< "$pwd $i" #Here I try to simulate a input on stdin
cat 1> /tmp/Frack/password
if ( grep -o "Wrong" { "$(</dev/fd/3)"; } 3<&1 ) #Here I try to check if there's an answer from the server, and if it contains the word "Wrong"
then
continue
else
echo "pass trouvé : $i" > /tmp/Frack/password #If I found the right pin code, I write it within this file (on which I gave the right access to be able to write on it).
break
fi
done
I add an example of what I'm supposed to read as an answer from my call to the server with a wrong pin code : "I am the pincode checker for user bandit25. Please enter the password for user bandit24 and the secret pincode on a single line, separated by a space.
Wrong! Please enter the correct pincode. Try again."

You could:
#!/bin/bash
# note stdout is connected to remote end
# note stdin reads from remote end
for i in {0001..9999}; do
# hacky hack to output to controlling terminal
echo "testing $i" > /dev/tty
# output the password to stdout
printf "%s\n" "password $i"
# read one line from stdin
if ! IFS= read -r line; then
echo "error" > /dev/tty
exit 1
fi
# if the word "wrong" case insensitive is missing from readed line
if ! grep -i -q 'wrong' <<<"$line"; then
# then we are finished
echo "I'm in" > /dev/tty
exit 0
fi
done
then I can:
$ nc -e /tmp/1.sh localhost 10000
testing 0001
testing 0002
testing 0003
testing 0004
I'm in
< --- snip -------- >
$ nc -v -v -l localhost -p 10000 <<<$' wrong \n wrong \n wrong \n ok'
Listening on any address 10000 (ndmp)
Connection from 127.0.0.1:39152
password 0001
password 0002
password 0003
password 0004
read(net): Connection reset by peer

Related

Pass variables out of an interactive session from bash script

Hello People of the world,
I am trying to write a script that will allow user to failover apps between sites in bash.
Our applications are controlled by Pacemaker and I thought I would be able to write a function that would take in the necessary variables and act. Stop on one site, start on another. Once I have ssh'd to the remote machine, I am unable to get the value of the grep/awk command back for the status of the application in PCS.
I am encountering a few issues, and have tried answers from stackoverflow and other sites.
I send the ssh command to /dev/null 2>&1 as banners pop up on screen that unix admin have on the local user and -q does not deal with it - Does this stop anything being returned?
when using awk '{print \\\\\\$4}' in the code, I get a "backslash not last character on line" error
To get round this, I tried result=$(sudo pcs status | grep nds_$resource), however this resulted in a password error on sudo
I have tried >/dev/tty and >$(tty)
I tried to not suppress the ssh (remove /dev/null 2>&1) and put the output in variable at function call, removing the awk from the sudo pcs status entry.
result=$(pcs_call "$site1" "1" "2" "disable" "pmr")
echo $result | grep systemd
This was OK, but when I added | awk '{print \\\$4}' I then got the fourth word in the banner.
Any help would be appreciated as I have been going at this for a few days now.
I have been looking at this answer from Bruno, but unsure how to implement as I have multiple sudo commands.
Below is my strip down of the function code for testing on one machine;
site1=lon
site2=ire
function pcs_call()
{
site=$1
serverA=$2
serverB=$3
activity=$4
resource=$5
ssh -tt ${site}servername0${serverA} <<SSH > /dev/null 2>&1
sudo pcs resource ${activity} proc_${resource}
sleep 10
sudo pcs status | grep proc_$resource | awk '{print \\\$4}' | tee $output
exit
SSH
echo $output
}
echo ====================================================================================
echo Shutting Down PMR in $site1
pcs_call "$site1" "1" "2" "disable" "pmr"
I'd say start by pasting the whole thing into ShellCheck.net and fixing errors until there are no suggestions, but there are some serious issues here shellcheck is not going to be able to handle alone.
> /dev/null says "throw away into the bitbucket any data that is returned. 2>&1 says "Send any useful error reporting on stderr wherever stdout is going". Your initial statement, intended to retrieve information from a remote system, is immediately discarding it. Unless you just want something to occur on the remote system that you don't want to know more about locally, you're wasting your time with anything after that, because you've dumped whatever it had to say.
You only need one backslash in that awk statement to quote the dollar sign on $4.
Unless you have passwordless sudo on the remote system, this is not going to work out for you. I think we need more info on that before we discuss it any deeper.
As long as the ssh call is throwing everything to /dev/null, nothing inside the block of code being passed is going to give you any results on the calling system.
In your code you are using $output, but it looks as if you intend for tee to be setting it? That's not how that works. tee's argument is a filename into which it expects to write a copy of the data, which it also streams to stdout (tee as in a "T"-joint, in plumbing) but it does NOT assign variables.
(As an aside, you aren't even using serverB yet, but you can add that back in when you get past the current issues.)
At the end you echo $output, which is probably empty, so it's basically just echo which won't send anything but a newline, which would just be sent back to the origin server and dumped in /dev/null, so it's all kind of pointless....
Let's clean up
sudo pcs status | grep proc_$resource | awk '{print \\\$4}' | tee $output
and try it a little differently, yes?
First, I'm going to assume you have passwordless sudo, otherwise there's a whole other conversation to work that out.
Second, it's generally an antipattern to use both grep AND awk in a pipeline, as they are both basically regex engines at heart. Choose one. If you can make grep do what you want, it's pretty efficient. If not, awk is super flexible. Please read the documentation pages on the tools you are using when something isn't working. A quick search for "bash man grep" or "awk manual" will quickly give you great resources, and you're going to want them if you're trying to do things this complex.
So, let's look at a rework, making some assumptions...
function pcs_call() {
local site="$1" serverA="$2" activity="$3" resource="$4" # make local and quotes habits you only break on purpose
ssh -qt ${site}servername0${serverA} "
sudo pcs resource ${activity} proc_${resource}; sleep 10; sudo pcs status;
" 2>&1 | awk -v resource="$resource" '$0~"proc_"resource { print $4 }'
}
pcs_call "$site1" 1 disable pmr # should print the desired field
If you want to cath the data in a variable to use later -
var1="$( pcs_call "$site1" 1 disable pmr )"
addendum
Addressing your question - use $(seq 1 10) or just {1..10}.
ssh -qt chis03 '
for i in {1..10}; do sudo pcs resource disable ipa $i; done;
sleep 10; sudo pcs status;
' 2>&1 | awk -v resource=ipa '$0~"proc_"resource { print $2" "$4 }'
It's reporting the awk first, because order of elements in a pipeline is "undefined", but the stdout of the ssh is plugged into the stdin of the awk (and since it was duped to stdout, so is the stderr), so they are running asynchronously/simultaneously.
Yes, since these are using literals, single quotes is simpler and effectively "better". If abstracting with vars, it doesn't change much, but switch back to double quotes.
# assuming my vars (svr, verb, target) preset in the context
ssh -qt $svr "
for i in {1..10}; do sudo pcs resource $verb $target \$i; done;
sleep 10; sudo pcs status;
" 2>&1 | awk -v resource="$target" '$0~"proc_"resource { print $2" "$4 }'
Does that help?

Netcat script to send an email when output file have a string

I'm trying to do a sh script to netcat an IP and to send an email when a the port is not answering.
So far I have this but altought the script is working and the output file in filled in, but I receive the mail is empty. I debug it by writing something with vi in the output file and I do get the email with what I've written.
Here's the script:
netcat -z -v -w x.x.x.x 23 > /path/resultt.txt 2>&1
if grep -q "timed out" /path/resultt.txt ;
then
ssmtp email#gmail.com < /path/resultt.txt
else
echo
fi
I think it has something to do with doing 2>&1
Your use of 2>&1 or "send stderr to the same place as stdout" should not be a problem. In cases where the last argument is a number, I like to make sure my redirects have the fd number on them. But, yours does work without it.
I'm using a public telnet based service for this demo. It should work for anyone, but as with all things free, it may not be around forever.
$ nc telnetmyip.com 23 > /tmp/out 2>&1
$ cat /tmp/out
{
"comment": "## Your IP Address is 96.67.120.126 (59742) ##",
"family": "ipv4",
"ip": "96.67.120.126",
"port": "59742",
"protocol": "telnet",
"version": "v1.1.0",
"website": "https://github.com/packetsar/checkmyip"
}
So, that is working exactly as intended.
Now, you say "but I receive the mail is empty". According to your code, an email is only sent if "timed out" is present in the file. So, the only way for an empty email to be sent is if you are not giving the correct file to ssmtp the correct way.
You may want to consider doing it like the example at https://wiki.archlinux.org/index.php/SSMTP#Sending_email
# only do this once
$ cat > header.txt <<'EOF'
To:email#gmail.com
From:youraccount#gmail.com
Subject: telnet resulted in a timeout
Hello, admin. I hope this doesn't ruin your day.
The command `netcat -z -v -w x.x.x.x 23 > /path/resultt.txt` resulted in this:
EOF
$ cat header.txt /path/resultt.txt | sendmail -t
I hope that helps. In closing I'll repeat: The only way for an empty email to be sent is if you are not giving the correct file to ssmtp the correct way. (Assuming that you have manually confirmed that ssmtp works in this environment.)

Linux and bash - How can I get the device name of an input device event?

I'm trying to automate the process of getting a game controller's device event and checking its name, then if the name matches a string, pass the event to another program.
By hand, I run evtest /dev/input/eventXX to check the name of the input device, then keyboard interrupt(ctrl-c) to stop it. If that device matches the name of a certain type of game controller, I run the command xboxdrv --evtest /dev/input/eventXX with the same event number.
However, because the "evtest" command continues to print the input device's outputs instead of terminating after printing the name and only exits on keyboard interrupt, I'm not sure how I could have the bash script get the name of the device.
I'm still learning bash so there may be syntax errors in my code. But, here it is:
#!/bin/sh
WiiUGCName="Wii U Gamecube Adapter Port"
#find the controller(s)
NumberOfEvents=$(ls /dev/input | grep -c event*)
echo "Number of input devices: $NumberOfEvents"
#launch xboxdrv for each controller
i=0
while [ $i < $NumberOfEvents ]; do
echo "loop"
OccurrencesOfName=$(evtest /dev/input/event$i | grep -c "$WiiUGCName")
echo "Occurrences: $OccurrencesOfName"
if [ $OccurrencesOfName>0 ]; then
echo "Controller found"
#launch xboxdrv here
else
echo "no controller found"
fi
let i=i+1
done
All this code actually does right now is hang at that evtest since it cannot be terminated.
The simplest option is to give evtest some reasonable time to print the interesting portion of the output, say 1 second, and then kill it. If you place that code in a shell function, the resulting code can remain readable. For example, replace:
OccurrencesOfName=$(evtest /dev/input/event$i | grep -c "$WiiUGCName")
with the invocation of a shell function:
OccurrencesOfName=$(evtest_and_exit | grep -c "$WiiUGCName")
evtest_and_exit can be defined as follows:
evtest_and_exit() {
local evtest_pid
evtest /dev/input/event$i &
evtest_pid=$!
sleep 1 # give evtest time to produce output
kill $evtest_pid
}

Replace login prompt with interactive bash script on serial port linux

I am working on a CentOS box.
What I expect: To run my own CLI/setup on startup instead of login prompt on serial console (telnet).
What I did so far:-
I changed call to "agetty" command in serial.conf and serial-ttyUSB0.conf files under /etc/init/, as follows:-
exec /sbin/agetty -n -l <path-to-my-custom-script> ........
My custom.sh script is:-
#!/bin/bash
LOOP_FLAG=0
while [ $LOOP_FLAG -eq 0 ]; do
for TTY in /dev/ttyS0 /dev/tty0; do
echo "Please choose to enter in 'setup' or 'cli'. (s/c)? " > $TTY
done
read sc
case $sc in
[Ss]* ) LOOP_FLAG=1; <some-executable-cli-file-path>; break;;
[Cc]* ) LOOP_FLAG=1; <some-executable-setup-file-path>; break;;
* ) for TTY in /dev/ttyS0 /dev/tty0; do
echo "Please press 's' or 'c'." >$TTY
done;;
esac
done
But when system boots, on a telnet session, I could only see the "Please choose to enter.." question on screen and after that I couldn't able to type anything on console.
One more update:
If I run the above agetty command on shell prompt as it is (say from ssh session), then it works fine on serial console (telnet). But, from the above startup scripts, it doesn't work.
Can anybody help me out with this?
Thanks in advance.
-Neo
Sorry I'm a few years late. Hopefully this will be of help for people searching for solution to this problem in the future.
The issue lies here:
-n, --skip-login Do not prompt the user for a login name. This can be used in connection with -l option to invoke a non-standard login
process such as a BBS system. Note that with the -n option, agetty
gets no input from user who logs in and therefore won't be able to
figure out parity, character size, and newline processing of the
connection. It defaults to space parity, 7 bit characters, and ASCII
CR (13) end-of-line character. Beware that the program that agetty
starts (usually /bin/login) is run as root.
So you need to initialize the terminal yourself in the script you are replacing the login prompt with. I found the settings below work well:
/bin/stty -F /dev/ttyO0 115200 cs8 sane
Remember to replace the baud rate and terminal name to your own.

Perform action when user logs in via SSH from a particular host

I have a quesiton that puzzles me and I wonder if anyone has attempted to achieve the following:
Let's assume that this is the result of my 'last' command in a Linux environment:
root pts/1 192.168.1.10 Wed Feb 10 07:04 - 07:57 (00:52)
root pts/2 Tue Feb 9 22:00 - 00:13 (02:13)
How can I setup a particular action (say for example a modified MOTD or sending an email) if the the 'root' user has logged in from 192.168.1.10. Is there a way of capturing this information?
The second part of this question is that how can I make the above check a bit more robust - i.e. if I have the following:
mary pts/1 192.168.1.10 Wed Feb 10 07:04 - 07:57 (00:52)
bob pts/2 Tue Feb 9 22:00 - 00:13 (02:13)
Now I'd like to perform an action if the username is equal to 'mary' and the host is 192.168.1.10.
Any suggestions are welcomed.
Thank you in advance.
There's a special file /etc/ssh/sshrc where you can put some commands that will runs each time someone connect by ssh. I wrote that for you :
#!/bin/bash
mail=user#domain.tld
monitored_user=root
monitored_ip=x.x.x.x
hostname=$(hostname)
# add a welcome message:
printf >&2 "\nWelcome on $hostname $USER\n"
read -d " " ip <<< $SSH_CONNECTION
[[ $ip == $monitored_ip && $USER == $monitored_user ]] || exit 0
date=$(date "+%d.%m.%Y %Hh%M")
reverse=$(dig -x $ip +short)
mail -s "Connexion of $USER on $hostname" $mail <<EOF
IP: $ip
Reverse: $reverse
Date: $date
EOF
Put this script in a file, then put the full path of the script in /etc/ssh/sshrc
In man ssh :
/etc/ssh/sshrc :
Commands in this file are executed by ssh when the user
logs in, just before the user's shell (or command) is started. See the
sshd(8) manual page for more information.
Thanks for all your replies. Eventually I managed to find a solution which does work for the time being but it does have one flaw which I'll point out in a minute.
I have added the following to my /etc/bashrc file (or /etc/bash.bashrc whatever environment you're using):
HOST="192.168.0.1"
RHOST=`who am i | sed -n 's/.*(\([^) ]*\).*/\1/p; 1q'`
if [ "$RHOST" == "$HOST" ]; then
echo "SAY WHAT!"
#add further actions here if needed
fi
The flaw that I was talking about before may actually not be a flaw. If you're already SSH-ed into the system, and you want to SSH to a host which lives on the same IP, say ssh root#your-host who am i would then print 'your-host' but I think that's the way it should be.
Needless to say that the above sed statement can be modified so you can capture the username as well, and you can extend the if/else statement to suite your needs.
Thank you again for all your replies.
You can add something to /etc/profile or equivalent that does something depending on the value of $SSH_CLIENT.
It looks like you are using last because it reads /var/log/wtmp by default which is a record of logins. The who command also allows you to read the same file but with an interface more to your needs.
For example:
$ who --ips /var/log/wtmp | grep '^msw.*127.0.0.1'
msw pts/2 2012-10-07 15:52 127.0.0.1
msw pts/3 2012-10-07 15:55 127.0.0.1
where neither of those sessions were active, but rather historic and logged.
In ubuntu i put a script in
/etc/profile.d
and when someone(user ssh) log in, it send an email to my mail
#/etc/profile.d/run_on_loggin.sh
echo $(who i am) | mail -s 'SSH Login Notification' mymail#hotmail.com
I want to create a php file with smtp, to send email with my mail to me...
some times hotmail saved in spam...
if i have the php file i will run like this...
if i want to pass var to file php run like this...
excuse my english :3
note: i think this command run from user, be carefully if the user doen't has permission to use some command or send email.
One way would be to run a simple script periodically:
#!/bin/bash
users=$(last | sed -ne '/192\.168\.1\.10/ s/\([^ ]*\).*/\1/p')
for user in $users; do
sendmail "$user" < email.txt
done
This would pipe the last command into sed to extract a user list and save it into the variable $users. The sed command uses the -n flag so it only prints what we tell it to. First, we select lines that contain the specified IP, with the /192\.168\.1\.10/ "address". On those lines, we attempt to extract the characters before a space, and if we succeed we print the result.
Then, we can loop through the $users variable and act accordingly.
One way to call this repeatedly would be through cron, and a simpler way would be to do while true; do ./my_script.bash; sleep 60; done.

Resources