I am trying to find an efficient way to do the level 5 of the OverTheWire bandit challenge.

Anyway, I have a bunch of files, and there is only one that respects the following criteria:

  • Human-readable
  • 1033 bytes in size
  • Non-executable

Right now, I am using the find command, and I am able to find the files matching the 2 last criteria:

find . -size 1033c ! -executable

However, I don't know how to excluse non-human-readable files. Solutions I found for that challenge use the -readable test parameter, but I don't think this works. -readable only looks at the files' permissions, and not at its content, while the challenge description ask for an ASCII file or something like that.

  • 1
    How do you define human readable? Not binary? – terdon Sep 30 '16 at 13:51
  • file command is your friend :) – Romeo Ninov Sep 30 '16 at 13:52
  • Maybe duplicate of: stackoverflow.com/questions/14505218/… – zuazo Sep 30 '16 at 13:55
  • @zuazo there's no such thing as cross-site duplicates. That post might have the answer, but it isn't a "duplicate". – terdon Sep 30 '16 at 13:56
  • 2
    Humans are one of the most intelligent known species on earth. They're also the only one known to versed with computers. They can read most files provided they can find out the type and get hold of the encryption keys for encrypted one. – Stéphane Chazelas Sep 30 '16 at 13:58

10 Answers 10

Yes, you can use find to look for non-executable files of the right size and then use file to check for ASCII. Something like:

find . -type f -size 1033c ! -executable -exec file {} + | grep ASCII

The question, however, isn't as simple as it sounds. 'Human readable' is a horribly vague term. Presumably, you mean text. OK, but what kind of text? Latin character ASCII only? Full Unicode? For example, consider these three files:

$ cat file1
abcde
$ cat file2
αβγδε
$ cat file3
abcde
αβγδε
$ cat file4
#!/bin/sh
echo foo

These are all text and human readable. Now, let's see what file makes of them:

$ file *
file1: ASCII text
file2: UTF-8 Unicode text
file3: UTF-8 Unicode text
file4: POSIX shell script, ASCII text executable

So, the find command above will only find file1 (for the sake of this example, let's imagine those files had 1033 characters). You could expand the find to look for the string text:

find . -type f -size 1033c ! -executable -exec file {} + | grep -w text

With the -w, grep will only print lines where text is found as a stand-alone word. That should be pretty close to what you want, but I can't guarantee that there is no other file type whose description might also include the string text.

While -exec is mostly used to do something with the files that where found, it can also act as a test. Therefore, we can add it to your other criteria:

find . \
  -size 1033c \
  -not -executable \
  -exec sh -c 'file {} | grep "text$"' \;

Remember, grep returns non-zero when the pattern wasn't found, and sh -c "COMMAND" will return the result of the evaluation (as long as it's valid). So this will only print files where file <filename> spits something out that ends with text, e.g. "UTF-8 Unicode text` or "ASCII text", but not "Non-ISO extended-ASCII text, with escape sequences".

In a single line, it even ends up shorter than going over xargs:

find . -size 1033c -not -executable -exec sh -c 'file {} | grep "text$"' \;

Keep in mind that you can replace sh -c 'file {} | grep "text$"' with any custom command. If you want to check for something very complex, it might be a better idea to provide a shell script and use that instead:

find . -size 1033c -not -executable -exec is_human_readable.sh {} \;

which, in the long run, is easier to maintain than your shell's history:

#!/bin/sh
file "$@" | grep "text$" > /dev/null
  • Nice! Note, however, that matching text$ will exclude things recognized as shell scripts. Anything with a shebang is identified as a script, and those are perfectly human readable. – terdon Sep 30 '16 at 14:39
  • @terdon true, but scripts tend to be executable :D. That being said, a proper script should also recognize PDFs. But on the other hand, is a PDF containing an image human readable? Is a PNG of some text readable? Probably. I guess a complet test will be… challenging. – Zeta Sep 30 '16 at 14:43
find . -size 1033c ! -executable -exec file {} +
find . -size 1033c ! -executable|xargs file|grep "ASCII text" |awk -F: '{print $1}'

Please try this combined commands. it works on my station.

You can try this

find . -size 1033c ! -executable -exec file {} +

Your challenge does not allows grep. password file will be reported as "ASCII text, with very long lines"

To filter out the human-readable file names, you can make use of the [:print:] (printable) character class name. You will find more about such classes in the manual for grep.

find . -type f -size 1033c -name "[[:print:]]*" ! -executable

On a second thought, the "human-readable" requirement might refer to the file's content, instead of its name. In other words, you would be searching for text files. That is a little more tricky. As @D_Bye suggested in a comment, you should then use the file command to determine the file content type. But it would not be a good idea to run file after a pipe, because it would complicate the task of displaying the file's name. Here's what I suggest:

find . -type f -size 1033c ! -executable -exec sh -c 'file -b $0 | grep -q text' {} \; -print

This is briefly how the file-part works:

  • The -exec predicate executes sh -c 'file -b $0 | grep -q text' FILENAME for each FILENAME that satisfies all the previous conditions (type, size, non-executable).
  • For each of those files, a shell (sh) runs this short script: file -b $0 | grep -q text, replacing $0 with the filename.
  • The file program determines the content type of each file and outputs this information. The -b option prevents printing the name of each tested file.
  • grep filters the output coming from file program, searching for lines containing "text". (See for yourself, how a typical output of the file command looks like.)
  • But grep does not output the filtered text, because it has the -q (quiet) option given. What it does, is just change its exit status to either 0 (which represents "true" - the filtered text was found) or 1 (meaning "error" - the text "text" did not appear in the output from file).
  • The true/false exit status coming from grep is passed further by sh to find and acts as the final result of the whole "-exec sh -c 'file $0 | grep -q text' {} \;" test.
  • In case the above test returned true, the -print command is executed (i.e. the name of the tested file is printed).

You only need to use:

find inhere -size 1033c

It will give you the only file that contains the password.

bandit4@bandit:~$ ls
inhere

bandit4@bandit:~$ file inhere/*


inhere/-file00: data
inhere/-file01: data
inhere/-file02: data
inhere/-file03: data
inhere/-file04: data
inhere/-file05: data
inhere/-file06: data
inhere/-file07: ASCII text
inhere/-file08: data
inhere/-file09: data

bandit4@bandit:~$ pwd 

/home/bandit4

bandit4@bandit:~$ cat /home/bandit4/inhere/-file07

koReBOKuIDDepwhWk7jZC0RTdopnAYKh
bandit4@bandit:~$ 
  • Simply use file inhere/* and cat /home/bandit4/inhere/-file07 – user234732 Jun 5 '17 at 22:41
find  -type f ! -executable -size 1033c

will get you the file from the exercise

du --human-readable | find -not -executable -size 1033c

will get your result

New contributor
Varun Rathore is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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