Are these awk commands vulnerable to code injection? - linux

I was unsure on how to correctly script a particular awk command which uses a shell variable, when I read the answers to How do I use shell variables in an awk script?.
The accepted answer demonstrates how interpolating a shell variable in an awkcommand would be prone to malicious code injection, and while I was able to reproduce the demo, I could not find the same problem with either of the following two commands:
#HWLINK=enp10s0
ip -o route | awk '/'$HWLINK'/ && ! /default/ {print $1}'
ip -o route | awk "/$HWLINK/"' && ! /default/ {print $1}'
So, the main question is if any of these (or both) is vulnerable.
A secondary question would be which form is preferred. I tried ip -o route | awk -v hwlink="$HWLINK" '/hwlink/ && ! /default/ {print $1}' but that doesn't work.
p.s. this is a refactoring; the original command was ip -o route | grep $HWLINK | grep -v default | awk '{print $1}'.

Sure, both are vulnerable, the first a bit less so.
This breaks your second line:
HWLINK="/{}BEGIN{print \"Your mother was a hamster and your father smelt of elderberries\"}/"
The only reason it doesn't break your first line is, in order to be able to be injected into the first line it must not contain spaces.
HWLINK="/{}BEGIN{print\"Your_mother_was_a_hamster_and_your_father_smelt_of_elderberries\"}/"
I see you already got the correct syntax to use :)

Your idea was right about letting the shell variables getting interpolated inside awk could let malicious code injection. As rightly pointed use the -v syntax, but your attempt fails because the pattern match with variable doesn't work in the form /../, use the direct ~ match
ip -o route | awk -v hwlink="$HWLINK" '$0 ~ hwlink && ! /default/ {print $1}'
Recommended way to sanitize your variables passed to awk would be to use the ARGV array or ENVIRON variable. Variables passed this way don't undergo expansion done by the shell
value='foo\n\n'
awk 'BEGIN {var=ARGV[1]; delete ARGV[1]}' "$value"
If you printed the value of var inside the awk it would be a literal foo\n\n and not the multi-line string which usually happens when the shell expands it.

Related

Calling a command variable on Bash [duplicate]

This question already has answers here:
Command not found error in Bash variable assignment
(5 answers)
Closed 4 years ago.
Im having trouble calling a variable that should bring out the output of a command.
#!/bin/bash
ipAddresses = 'ifconfig | awk -v OFS=": " -v RS= '$1!="lo" && split($0, a, /inet addr:/) > 1{sub(/ .*/, "", a[2]); print $1, a[2]}''
echo -e "Sus direcciones IP son: \n " $(ipAddresses)
Appreciating any advice
Variable assignments cannot have space around the = in the shell. Also, you don't want single quotes there, you want either backticks or $(). The single quotes should only be for your awk command. Your awk is needlessly complicated as well, and you are using command substitution ($()) when printing, but ipAdresses is a variable, not a command.
Try something like this:
#!/bin/bash
ipAddresses=$(ifconfig | sed 's/^ *//' | awk -F'[: ]' '/^ *inet addr:/{print $3}')
printf 'Sus direcciones IP son:\n%s\n' "$ipAddresses"
But that is really not portable. You didn't mention your OS, but I am assuming it's a Linux and the output suggests Ubuntu (I don't have addr after inet in the output of ifconfig on my Arch, for example).
If you are running Linux, you could use grep instead:
ipAddresses=$(ifconfig | grep -oP 'inet addr:\K\S+')
ip is generally replacing ifconfig, so try this instead:
ipAddresses=$(ip addr | awk '/inet /{print $2}')
or
ipAddresses=$(ip addr | grep -oP 'inet \K\S+')
Or, to remove the trailing /N:
ipAddresses=$(ip addr | grep -oP 'inet \K[\d.]+')
And you don't need the variable anyway, you can just:
printf 'Sus direcciones IP son:\n%s\n' "$(ip addr | awk '/inet /{print $2}')"
I am not sure about your intention, since they are not stated, so I am trying to guess them from the script.
Option 1: you are trying to get IP address to into the variable ipAddresses and that is not happenning.
Start by changing single quotes around the long command and debug the command.
Option 2: you are storing a command in variable ipAddresses that you want to execute on the second line.
For both of the options you need to use the the value of the variable through $ipAdresses on the second line.
Also fix the assignment to following formart:
varName="value" # Note no spaces around = sign
Replace the final $(ipAddresses) with ${ipAddresses} or just "$ipAddresses", but also save the output of your command using $().
Check Difference between ${} and $() in Bash.
A basic example:
#!/bin/sh
OUTPUT=$(uname -a)
echo "The output: $OUTPUT"

No such file or directory when populating variable in Bash

When I run the following command in a bash script:
urlOfServer="$($repoURL_StripLastFour | awk -F'/' '{print $1}')"
I get the following error:
192.168.1.12:7999/pcfpt/scriptsforexamples: No such file or directory
You can see that the value of the repoURL_StripLastFour variable is 192.168.1.12:7999/pcfpt/scriptsforexamples at the time when the script is run. This value is auto-created at runtime by other elements of the script, so I cannot simply pass it as a literal.
What specific syntax is required to resolve this error, so that the urlOfServer variable can be successfully populated?
I have tried many variations of moving quotes and parentheses already.
Replace
$repoURL_StripLastFour
with
echo "$repoURL_StripLastFour"
to feed awk from stdin or replace
$repoURL_StripLastFour | awk -F'/' '{print $1}'
with
awk -F'/' '{print $1}' <<< "$repoURL_StripLastFour"
to use a here string.
Don't use awk; just use parameter expansion:
urlOfServer=${repoURL_StripLastFour%%/*}
%%/* strips the longest suffix that matches /*, namely the first / and everything after it, leaving only the text preceding the first /.

Can I use a variable as parameter to AWK's {print}?

I have this bash statement for printing a specific cell from a .csv file.
set `cat $filename | awk -v FS=',' '{print $2}' | head -5 | tail -n 1`
The '{print $2}' part determines the column and the head -5 part determines the row.
Can I substitute a $counter variable in place of $2 (e.g., '{print $counter}')?
The answer is "yes" -- and there are a couple ways to do what you want. The proper way is to declare an awk variable using -v:
awk -F',' -v c=$counter 'NR==6 { print $c; exit }' "$filename"
(You will forgive me for moving some things around to do everything in awk, for passing "$filename" to awk safely, and for getting rid of set and back ticks -- that were doing nothing for the cause.)
Another way to do this is a bit of a "hackish" way -- leveraging shell quoting rules. This method requires some escaping to ensure that the first $ character (that references the intended field in awk) is not interpreted by the shell... The following works in bash (and POSIX sh):
awk -F',' "NR==6 { print \$$counter; exit }" "$filename"
Yes and all pipes could be removed. Variables are passed to awk with -v var=value.
Give a try to this tested version. Provide a value to the ̀€col and row variables:
set $(awk -F "," -v col=2 -v row=5 'NR==row {print $col; exit}' "${filename}")
$(command) is prefered to `command`, this later is deprecated.
NR is the current line number.
"${filename}" is expanded by the shell to its value: the double quotes will help if the filename contains some special chars.

Single Quote issue with gawk and shell script

I am writing a small script to map all the current memory being used by services running in a server. However, I am facing a problem doing that. My script is quite simple. I'm using pmap to find out memory being used and trying add up all the pid of a service running.
#!/bin/bash
result=`$pgrep java`
wc=`$pmap -x $result | wc -l`
gawk=`$pmap -x $result | gawk 'NR==$wc{print $3}'`
echo "$gawk"
Now, my problem is that gawk uses single quote when searching for a specific pattern (gawk 'NR==$wc{print $3}') but shell script gives me error because then meaning of single quote is different in shell from gawk.
Based on your comment, it looks like you're trying to do this:
pmap -x "$(pgrep java)" | awk '{s=$3}END{print s}'
This prints the third column of the last line of the output of pmap -x, with the PID of the running java process. In some versions of awk, you can simply do 'END{print $3}' but this isn't guaranteed to work.
pmap -x $result | gawk 'NR==$wc{print $3}' is not doing what you think it is. (I have replaced your $pmap with pmap, but my analysis is only of the gawk command so if that is incorrect it should be irrelevant.) The shell is going to pass the literal string NR==$wc{print $3} to awk, but it appears that you want awk to see the value of the shell variable $wc rather than the literal string $wc. When awk sees $wc, it treats wc an an uninitialized value, so $wc become equivalent to $0, and awk will print any line whose content matches the line number. The standard way to pass the shell variable into awk is:
pmap -x $result | gawk 'NR==w{print $3}' w=$wc
This assignes the shell variable wc to the awk variable w, and will print the third column of that line.
Note that there are a number of issues with this shell script, but this seems to be the core confusion.

How to pass AWK output into variable?

I have a small bash script that greps/awk paragraph by using a keyword.
But after adding in the extra codes : set var = "(......)" it only prints a blank line and not the paragraph.
So I would like to ask if anyone knows how to properly pass the awk output into a variable for outputting?
My codes:
#!/bin/sh
set var = "(awk 'BEGIN{RS=ORS="\n\n";FS=OFS="\n"}/FileHeader/' /root/Desktop
/logs/Default.log)"
echo $var;
Thanks!
Use command substitution to capture the output of a process.
#!/bin/sh
VAR="$(awk 'BEGIN{RS=ORS="\n\n";FS=OFS="\n"}/FileHeader/' /root/Desktop/logs/Default.log)"
echo "$VAR"
some general advice with regards to shell scripting:
(almost) always quote every variable reference.
never put spaces around the equals sign in variable assignment.
You need to use "command substitution". Place the command inside either backticks, `COMMAND` or, in a pair of parentheses preceded by a dollar sign, $(COMMAND).
To set a variable you don't use set and you can't have spaces before and after the =.
Try this:
var=$(awk 'BEGIN{RS=ORS="\n\n";FS=OFS="\n"}/FileHeader/' /root/Desktop/logs/Default.log)
echo $var
You gave me the idea of this for killing a process :). Just chromium to whatever process you wanna kill.
Try this:
VAR=$(ps -ef | grep -i chromium | awk '{print $2}'); kill -9 $VAR 2>/dev/null; unset VAR;
anytime you see grep piped to awk, you can drop the grep. for the above,
awk '/^password/ {print $2}'
awk can easily replace any text command like cut, tail, wc, tr etc. and especally multiple greps piped next to each other. i.e
grep some_co.mand | a | grep b ... to | awk '/a|b|and so on/ {some action}.
Try to create a variable coming from vault/Hashicorp, when using packer template variables, like so:
BUILD_PASSWORD=$(vault read secret/buildAccount| grep ^password | awk '{print $2}')
echo $BUILD_PASSWORD
You can to the same with grep ^user

Resources