As a quick first pass, the file command can quickly detect image headers:
if file "$FILE" |grep -qE 'image|bitmap'; then echo "File '$FILE' has the headers of an image" fi
(The second alternation for bitmap is needed if you want to recognize Windows BMP files since libmagic does not use the word "image" to describe bitmap images.)
However, we can trick file with the PHP-based fake image from Bruce Ediger's answer:
$ echo 'GIF87a<?php echo "Hello from PHP!"; ?>' > fake.gif $ file fake.gif && echo image detected || echo no image detected fake.gif: GIF image data, version 87a, 16188 x 26736 image detected
Using Imagemagick identify
The ImageMagick suite has an identify script with a CLI frontend that will return some metadata on a given image. It fails when the expected metadata is not present, so it is ideal for this purpose:
$ identify fake.gif && echo image detected || echo no image detected identify-im6.q16: negative or zero image size `fake.gif' @ error/gif.c/ReadGIFImage/1402. no image detected
For faster scanning of a large collection of files, I recommend putting both together:
if file "$FILE" |grep -qE 'image|bitmap' \ && ! identify "$FILE" >/dev/null 2>&1; then echo "File '$FILE' is a fake image!" fi
(This redirects the output of identify into oblivion since we only care about whether it was able to complete successfully, which is captured by its exit code.)
Even this can still be tricked
The following example uses a simple 1x1 white GIF with the same PHP code added to the end. I don't know PHP and I'm not sure this will actually run, but since PHP is a template language that prints the literal "text" to anything outside its <?php … ?> tag, I assume that this will run the given code as-is, with merely some garbage before the payload.
$ { echo 'R0lGODdhAQABAIAAAP///////ywAAAAAAQABAAACAkQBAD' echo 's8P3BocCBlY2hvICJIZWxsbyBmcm9tIFBIUCEiOyA/Pgo=' } | base64 -d > fake2.gif $ strings fake2.gif GIF87a ;<?php echo "Hello from PHP!"; ?> $ file fake2.gif fake2.gif: GIF image data, version 87a, 1 x 1 $ identify fake2.gif fake2.gif GIF 1x1 1x1+0+0 8-bit sRGB 2c 68B 0.000u 0:00.000
This can also be done with a GIF comment to be fully valid as an image:
$ hd fake3.gif 00000000 47 49 46 38 39 61 01 00 01 00 80 00 00 ff ff ff |GIF89a..........| 00000010 ff ff ff 21 fe 20 3c 3f 70 68 70 20 65 63 68 6f |...!. <?php echo| 00000020 20 22 48 65 6c 6c 6f 20 66 72 6f 6d 20 50 48 50 | "Hello from PHP| 00000030 21 22 3b 20 3f 3e 00 2c 00 00 00 00 01 00 01 00 |!"; ?>.,........| 00000040 00 02 02 44 01 00 3b |...D..;| 00000047
I've picked on GIF and taken advantage of its comment system, but just concatenating a payload after any image should also work to bypass this detection technique. It's merely harder than fooling file and (depending on the implementation) it might leave some evidence behind (the garbage from the image).
file *to check file types