8

I have one quick question.
Is it normal that bash (i am using 4.4.11) is not displaying lines/text that is separated / end with plain \r ?

I was a bit surprised to see this behavior:

$ a=$(printf "hello\ragain\rgeorge\r\n")
$ echo "$a"
george

But "hello again" text is still there,somehow "hidden":

$ echo "$a" |od -w32 -t x1c
0000000  68  65  6c  6c  6f  0d  61  67  61  69  6e  0d  67  65  6f  72  67  65  0d  0a
          h   e   l   l   o  \r   a   g   a   i   n  \r   g   e   o   r   g   e  \r  \n

And as soon as we just play with bash is fine.... But is this a potential security risk? What if contents of variable "a" come from outter world and include "bad commands" instead of just hello?

Another test, a bit unsecure this time:

$ a=$(printf "ls;\rGeorge\n")
$ echo "$a"
George
$ eval "$a"
0                   awkprof.out       event-tester.log  helloworld.c      oneshot.sh         rightclick-tester.py  tmp                    uinput-simple.py
<directory listing appears with an error message at the end for command George>

Imagine a hidden rm instead of a hidden ls.

Same behavior when using echo -e:

$ a=$(echo -e "ls;\rGeorge\r\n"); echo "$a"
George

Is it me that does something wrong...?

17

Your echo "$a" prints "hello", then goes back to the beginning of the line (which is what \r does), print "again", goes back again, prints "george", goes back again, and goes to the next line (\n). It’s all perfectly normal, but as chepner points out, it doesn’t have anything to do with Bash: \r and \n are interpreted by the terminal, not by Bash (which is why you get the full output when you pipe the command to od).

You can see this better with

$ a=$(printf "hellooooo\r  again,\rgeorge\r\n")
$ echo "$a"

since that will leave the end of the overwritten text:

georgen,o

You can’t really use that to hide commands though, only their output (and only if you can be sure to overwrite with enough characters), unless using eval as you show (but using eval is generally not recommended). A more dangerous trick is using CSS to mask commands intended to be copied and pasted from web sites.

  • Clear enough, thanks. I was lucky/unlucky to use a last word in my test capable to overwrite completely the previous two words. – George Vasiliou Apr 3 '17 at 9:31
  • 2
    Note that this isn't really anything to do with bash, though; it is entirely up to the terminal how to "display" the carriage return, or how to display the overwritten characters. For example, it would be perfectly valid for a terminal to display -\r| as something resembling a plus sign, rather than just a pipe. – chepner Apr 3 '17 at 14:07
  • @chepner Good to know! Thanks for sharing. I thought that this was just a bash behavior. – George Vasiliou Apr 3 '17 at 14:27
  • @GeorgeVasiliou Nope, bash just writes bytes to a file; it's up to whoever reads those bytes to interpret them. – chepner Apr 3 '17 at 14:28
  • Nitpick: printf, which is a bash built-in command (although it also exists as a standalone executable), interprets the \r (a sequence of two bytes: 0x5c and 0x72 and produces the carriage return (a single byte: 0x0d). Then, yes, the terminal interprets the byte 0x0d in a special way, but it does not interpret the original string \r. – Georges Dupéron Feb 15 at 9:52
5

In the Unix world, a carriage return (commonly encoded as \r in programming languages) is an unremarkable control character. You can have carriage returns inside a line of text, like any other character apart from a line feed (also called newline), which marks the end of a line.

In particular, in a bash script, a carriage return is an ordinary word constituent character, like letters and digits. Any special effect of the carriage return comes from the terminal, not from the shell.

A carriage return is a control character. When you print it to a terminal, instead of displaying a glyph, the terminal performs some special effect. For a carriage return, the special effect is to move the cursor to the beginning of the current line. Thus, if you print a line that contains a carriage return in the middle, then the effect is that the second half is written over the first half.

Several other control characters have special effects: the backspace character moves the cursor left by one position. The bell character causes the terminal to emit a sound or otherwise attract the user's attention. The escape character starts an escape sequence which can have all kinds of special effects.

If you display untrusted output, you need to scrub or escape control characters. Not only carriage returns, but also several others, in particular the escape character, which can cause all kinds of bad effects. See Can “cat-ing” a file be a potential security risk? and How to avoid escape sequence attacks in terminals? for more on the topic.

-1

You can view the carriage returns in a bash variable using the printf function with a %q format.

$ TESTVAR="$(printf ' Version: 1 \r Build: 20180712 \r Test: 1324')"

$ printf %q $TESTVAR
Version:1$'\r'Build:20180712$'\r'Test:1324

Sources and further reading:

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.