From 0d47952611198ef6b1163f366dc03922d20b1475 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 09:42:04 +0200 Subject: Adding upstream version 7.94+git20230807.3be01efb1+dfsg. Signed-off-by: Daniel Baumann --- scripts/http-exif-spider.nse | 539 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 539 insertions(+) create mode 100644 scripts/http-exif-spider.nse (limited to 'scripts/http-exif-spider.nse') diff --git a/scripts/http-exif-spider.nse b/scripts/http-exif-spider.nse new file mode 100644 index 0000000..ab6a12a --- /dev/null +++ b/scripts/http-exif-spider.nse @@ -0,0 +1,539 @@ +description = [[ +Spiders a site's images looking for interesting exif data embedded in +.jpg files. Displays the make and model of the camera, the date the photo was +taken, and the embedded geotag information. +]] + +--- +-- @usage +-- nmap --script http-exif-spider -p80,443 +-- +-- @output +-- PORT STATE SERVICE REASON +-- 80/tcp open http syn-ack +-- | http-exif-spider: +-- | http://www.javaop.com/Nationalmuseum.jpg +-- | Make: Canon +-- | Model: Canon PowerShot S100\xB4 +-- | Date: 2003:03:29 13:35:40 +-- | http://www.javaop.com/topleft.jpg +-- |_ GPS: 49.941250,-97.206189 - https://maps.google.com/maps?q=49.94125,-97.20618863493 +-- +-- @args http-exif-spider.url the url to start spidering. This is a URL +-- relative to the scanned host eg. /default.html (default: /) + +author = "Ron Bowes" +license = "Same as Nmap--See https://nmap.org/book/man-legal.html" +categories = {"intrusive"} + +local shortport = require 'shortport' +local stdnse = require 'stdnse' +local httpspider = require 'httpspider' +local string = require 'string' +local table = require 'table' + +-- These definitions are copied/pasted/reformatted from the jhead-2.96 sourcecode +-- (the code is effectively public domain, but credit where credit's due!) +TAG_INTEROP_INDEX = 0x0001 +TAG_INTEROP_VERSION = 0x0002 +TAG_IMAGE_WIDTH = 0x0100 +TAG_IMAGE_LENGTH = 0x0101 +TAG_BITS_PER_SAMPLE = 0x0102 +TAG_COMPRESSION = 0x0103 +TAG_PHOTOMETRIC_INTERP = 0x0106 +TAG_FILL_ORDER = 0x010A +TAG_DOCUMENT_NAME = 0x010D +TAG_IMAGE_DESCRIPTION = 0x010E +TAG_MAKE = 0x010F +TAG_MODEL = 0x0110 +TAG_SRIP_OFFSET = 0x0111 +TAG_ORIENTATION = 0x0112 +TAG_SAMPLES_PER_PIXEL = 0x0115 +TAG_ROWS_PER_STRIP = 0x0116 +TAG_STRIP_BYTE_COUNTS = 0x0117 +TAG_X_RESOLUTION = 0x011A +TAG_Y_RESOLUTION = 0x011B +TAG_PLANAR_CONFIGURATION = 0x011C +TAG_RESOLUTION_UNIT = 0x0128 +TAG_TRANSFER_FUNCTION = 0x012D +TAG_SOFTWARE = 0x0131 +TAG_DATETIME = 0x0132 +TAG_ARTIST = 0x013B +TAG_WHITE_POINT = 0x013E +TAG_PRIMARY_CHROMATICITIES = 0x013F +TAG_TRANSFER_RANGE = 0x0156 +TAG_JPEG_PROC = 0x0200 +TAG_THUMBNAIL_OFFSET = 0x0201 +TAG_THUMBNAIL_LENGTH = 0x0202 +TAG_Y_CB_CR_COEFFICIENTS = 0x0211 +TAG_Y_CB_CR_SUB_SAMPLING = 0x0212 +TAG_Y_CB_CR_POSITIONING = 0x0213 +TAG_REFERENCE_BLACK_WHITE = 0x0214 +TAG_RELATED_IMAGE_WIDTH = 0x1001 +TAG_RELATED_IMAGE_LENGTH = 0x1002 +TAG_CFA_REPEAT_PATTERN_DIM = 0x828D +TAG_CFA_PATTERN1 = 0x828E +TAG_BATTERY_LEVEL = 0x828F +TAG_COPYRIGHT = 0x8298 +TAG_EXPOSURETIME = 0x829A +TAG_FNUMBER = 0x829D +TAG_IPTC_NAA = 0x83BB +TAG_EXIF_OFFSET = 0x8769 +TAG_INTER_COLOR_PROFILE = 0x8773 +TAG_EXPOSURE_PROGRAM = 0x8822 +TAG_SPECTRAL_SENSITIVITY = 0x8824 +TAG_GPSINFO = 0x8825 +TAG_ISO_EQUIVALENT = 0x8827 +TAG_OECF = 0x8828 +TAG_EXIF_VERSION = 0x9000 +TAG_DATETIME_ORIGINAL = 0x9003 +TAG_DATETIME_DIGITIZED = 0x9004 +TAG_COMPONENTS_CONFIG = 0x9101 +TAG_CPRS_BITS_PER_PIXEL = 0x9102 +TAG_SHUTTERSPEED = 0x9201 +TAG_APERTURE = 0x9202 +TAG_BRIGHTNESS_VALUE = 0x9203 +TAG_EXPOSURE_BIAS = 0x9204 +TAG_MAXAPERTURE = 0x9205 +TAG_SUBJECT_DISTANCE = 0x9206 +TAG_METERING_MODE = 0x9207 +TAG_LIGHT_SOURCE = 0x9208 +TAG_FLASH = 0x9209 +TAG_FOCALLENGTH = 0x920A +TAG_SUBJECTAREA = 0x9214 +TAG_MAKER_NOTE = 0x927C +TAG_USERCOMMENT = 0x9286 +TAG_SUBSEC_TIME = 0x9290 +TAG_SUBSEC_TIME_ORIG = 0x9291 +TAG_SUBSEC_TIME_DIG = 0x9292 +TAG_WINXP_TITLE = 0x9c9b +TAG_WINXP_COMMENT = 0x9c9c +TAG_WINXP_AUTHOR = 0x9c9d +TAG_WINXP_KEYWORDS = 0x9c9e +TAG_WINXP_SUBJECT = 0x9c9f +TAG_FLASH_PIX_VERSION = 0xA000 +TAG_COLOR_SPACE = 0xA001 +TAG_PIXEL_X_DIMENSION = 0xA002 +TAG_PIXEL_Y_DIMENSION = 0xA003 +TAG_RELATED_AUDIO_FILE = 0xA004 +TAG_INTEROP_OFFSET = 0xA005 +TAG_FLASH_ENERGY = 0xA20B +TAG_SPATIAL_FREQ_RESP = 0xA20C +TAG_FOCAL_PLANE_XRES = 0xA20E +TAG_FOCAL_PLANE_YRES = 0xA20F +TAG_FOCAL_PLANE_UNITS = 0xA210 +TAG_SUBJECT_LOCATION = 0xA214 +TAG_EXPOSURE_INDEX = 0xA215 +TAG_SENSING_METHOD = 0xA217 +TAG_FILE_SOURCE = 0xA300 +TAG_SCENE_TYPE = 0xA301 +TAG_CFA_PATTERN = 0xA302 +TAG_CUSTOM_RENDERED = 0xA401 +TAG_EXPOSURE_MODE = 0xA402 +TAG_WHITEBALANCE = 0xA403 +TAG_DIGITALZOOMRATIO = 0xA404 +TAG_FOCALLENGTH_35MM = 0xA405 +TAG_SCENE_CAPTURE_TYPE = 0xA406 +TAG_GAIN_CONTROL = 0xA407 +TAG_CONTRAST = 0xA408 +TAG_SATURATION = 0xA409 +TAG_SHARPNESS = 0xA40A +TAG_DISTANCE_RANGE = 0xA40C +TAG_IMAGE_UNIQUE_ID = 0xA420 + +TagTable = {} +TagTable[TAG_INTEROP_INDEX] = "InteropIndex" +TagTable[TAG_INTEROP_VERSION] = "InteropVersion" +TagTable[TAG_IMAGE_WIDTH] = "ImageWidth" +TagTable[TAG_IMAGE_LENGTH] = "ImageLength" +TagTable[TAG_BITS_PER_SAMPLE] = "BitsPerSample" +TagTable[TAG_COMPRESSION] = "Compression" +TagTable[TAG_PHOTOMETRIC_INTERP] = "PhotometricInterpretation" +TagTable[TAG_FILL_ORDER] = "FillOrder" +TagTable[TAG_DOCUMENT_NAME] = "DocumentName" +TagTable[TAG_IMAGE_DESCRIPTION] = "ImageDescription" +TagTable[TAG_MAKE] = "Make" +TagTable[TAG_MODEL] = "Model" +TagTable[TAG_SRIP_OFFSET] = "StripOffsets" +TagTable[TAG_ORIENTATION] = "Orientation" +TagTable[TAG_SAMPLES_PER_PIXEL] = "SamplesPerPixel" +TagTable[TAG_ROWS_PER_STRIP] = "RowsPerStrip" +TagTable[TAG_STRIP_BYTE_COUNTS] = "StripByteCounts" +TagTable[TAG_X_RESOLUTION] = "XResolution" +TagTable[TAG_Y_RESOLUTION] = "YResolution" +TagTable[TAG_PLANAR_CONFIGURATION] = "PlanarConfiguration" +TagTable[TAG_RESOLUTION_UNIT] = "ResolutionUnit" +TagTable[TAG_TRANSFER_FUNCTION] = "TransferFunction" +TagTable[TAG_SOFTWARE] = "Software" +TagTable[TAG_DATETIME] = "DateTime" +TagTable[TAG_ARTIST] = "Artist" +TagTable[TAG_WHITE_POINT] = "WhitePoint" +TagTable[TAG_PRIMARY_CHROMATICITIES]= "PrimaryChromaticities" +TagTable[TAG_TRANSFER_RANGE] = "TransferRange" +TagTable[TAG_JPEG_PROC] = "JPEGProc" +TagTable[TAG_THUMBNAIL_OFFSET] = "ThumbnailOffset" +TagTable[TAG_THUMBNAIL_LENGTH] = "ThumbnailLength" +TagTable[TAG_Y_CB_CR_COEFFICIENTS] = "YCbCrCoefficients" +TagTable[TAG_Y_CB_CR_SUB_SAMPLING] = "YCbCrSubSampling" +TagTable[TAG_Y_CB_CR_POSITIONING] = "YCbCrPositioning" +TagTable[TAG_REFERENCE_BLACK_WHITE] = "ReferenceBlackWhite" +TagTable[TAG_RELATED_IMAGE_WIDTH] = "RelatedImageWidth" +TagTable[TAG_RELATED_IMAGE_LENGTH] = "RelatedImageLength" +TagTable[TAG_CFA_REPEAT_PATTERN_DIM]= "CFARepeatPatternDim" +TagTable[TAG_CFA_PATTERN1] = "CFAPattern" +TagTable[TAG_BATTERY_LEVEL] = "BatteryLevel" +TagTable[TAG_COPYRIGHT] = "Copyright" +TagTable[TAG_EXPOSURETIME] = "ExposureTime" +TagTable[TAG_FNUMBER] = "FNumber" +TagTable[TAG_IPTC_NAA] = "IPTC/NAA" +TagTable[TAG_EXIF_OFFSET] = "ExifOffset" +TagTable[TAG_INTER_COLOR_PROFILE] = "InterColorProfile" +TagTable[TAG_EXPOSURE_PROGRAM] = "ExposureProgram" +TagTable[TAG_SPECTRAL_SENSITIVITY] = "SpectralSensitivity" +TagTable[TAG_GPSINFO] = "GPS Dir offset" +TagTable[TAG_ISO_EQUIVALENT] = "ISOSpeedRatings" +TagTable[TAG_OECF] = "OECF" +TagTable[TAG_EXIF_VERSION] = "ExifVersion" +TagTable[TAG_DATETIME_ORIGINAL] = "DateTimeOriginal" +TagTable[TAG_DATETIME_DIGITIZED] = "DateTimeDigitized" +TagTable[TAG_COMPONENTS_CONFIG] = "ComponentsConfiguration" +TagTable[TAG_CPRS_BITS_PER_PIXEL] = "CompressedBitsPerPixel" +TagTable[TAG_SHUTTERSPEED] = "ShutterSpeedValue" +TagTable[TAG_APERTURE] = "ApertureValue" +TagTable[TAG_BRIGHTNESS_VALUE] = "BrightnessValue" +TagTable[TAG_EXPOSURE_BIAS] = "ExposureBiasValue" +TagTable[TAG_MAXAPERTURE] = "MaxApertureValue" +TagTable[TAG_SUBJECT_DISTANCE] = "SubjectDistance" +TagTable[TAG_METERING_MODE] = "MeteringMode" +TagTable[TAG_LIGHT_SOURCE] = "LightSource" +TagTable[TAG_FLASH] = "Flash" +TagTable[TAG_FOCALLENGTH] = "FocalLength" +TagTable[TAG_MAKER_NOTE] = "MakerNote" +TagTable[TAG_USERCOMMENT] = "UserComment" +TagTable[TAG_SUBSEC_TIME] = "SubSecTime" +TagTable[TAG_SUBSEC_TIME_ORIG] = "SubSecTimeOriginal" +TagTable[TAG_SUBSEC_TIME_DIG] = "SubSecTimeDigitized" +TagTable[TAG_WINXP_TITLE] = "Windows-XP Title" +TagTable[TAG_WINXP_COMMENT] = "Windows-XP comment" +TagTable[TAG_WINXP_AUTHOR] = "Windows-XP author" +TagTable[TAG_WINXP_KEYWORDS] = "Windows-XP keywords" +TagTable[TAG_WINXP_SUBJECT] = "Windows-XP subject" +TagTable[TAG_FLASH_PIX_VERSION] = "FlashPixVersion" +TagTable[TAG_COLOR_SPACE] = "ColorSpace" +TagTable[TAG_PIXEL_X_DIMENSION] = "ExifImageWidth" +TagTable[TAG_PIXEL_Y_DIMENSION] = "ExifImageLength" +TagTable[TAG_RELATED_AUDIO_FILE] = "RelatedAudioFile" +TagTable[TAG_INTEROP_OFFSET] = "InteroperabilityOffset" +TagTable[TAG_FLASH_ENERGY] = "FlashEnergy" +TagTable[TAG_SPATIAL_FREQ_RESP] = "SpatialFrequencyResponse" +TagTable[TAG_FOCAL_PLANE_XRES] = "FocalPlaneXResolution" +TagTable[TAG_FOCAL_PLANE_YRES] = "FocalPlaneYResolution" +TagTable[TAG_FOCAL_PLANE_UNITS] = "FocalPlaneResolutionUnit" +TagTable[TAG_SUBJECT_LOCATION] = "SubjectLocation" +TagTable[TAG_EXPOSURE_INDEX] = "ExposureIndex" +TagTable[TAG_SENSING_METHOD] = "SensingMethod" +TagTable[TAG_FILE_SOURCE] = "FileSource" +TagTable[TAG_SCENE_TYPE] = "SceneType" +TagTable[TAG_CFA_PATTERN] = "CFA Pattern" +TagTable[TAG_CUSTOM_RENDERED] = "CustomRendered" +TagTable[TAG_EXPOSURE_MODE] = "ExposureMode" +TagTable[TAG_WHITEBALANCE] = "WhiteBalance" +TagTable[TAG_DIGITALZOOMRATIO] = "DigitalZoomRatio" +TagTable[TAG_FOCALLENGTH_35MM] = "FocalLengthIn35mmFilm" +TagTable[TAG_SUBJECTAREA] = "SubjectArea" +TagTable[TAG_SCENE_CAPTURE_TYPE] = "SceneCaptureType" +TagTable[TAG_GAIN_CONTROL] = "GainControl" +TagTable[TAG_CONTRAST] = "Contrast" +TagTable[TAG_SATURATION] = "Saturation" +TagTable[TAG_SHARPNESS] = "Sharpness" +TagTable[TAG_DISTANCE_RANGE] = "SubjectDistanceRange" +TagTable[TAG_IMAGE_UNIQUE_ID] = "ImageUniqueId" + +GPS_TAG_VERSIONID = 0X00 +GPS_TAG_LATITUDEREF = 0X01 +GPS_TAG_LATITUDE = 0X02 +GPS_TAG_LONGITUDEREF = 0X03 +GPS_TAG_LONGITUDE = 0X04 +GPS_TAG_ALTITUDEREF = 0X05 +GPS_TAG_ALTITUDE = 0X06 +GPS_TAG_TIMESTAMP = 0X07 +GPS_TAG_SATELLITES = 0X08 +GPS_TAG_STATUS = 0X09 +GPS_TAG_MEASUREMODE = 0X0A +GPS_TAG_DOP = 0X0B +GPS_TAG_SPEEDREF = 0X0C +GPS_TAG_SPEED = 0X0D +GPS_TAG_TRACKREF = 0X0E +GPS_TAG_TRACK = 0X0F +GPS_TAG_IMGDIRECTIONREF = 0X10 +GPS_TAG_IMGDIRECTION = 0X11 +GPS_TAG_MAPDATUM = 0X12 +GPS_TAG_DESTLATITUDEREF = 0X13 +GPS_TAG_DESTLATITUDE = 0X14 +GPS_TAG_DESTLONGITUDEREF = 0X15 +GPS_TAG_DESTLONGITUDE = 0X16 +GPS_TAG_DESTBEARINGREF = 0X17 +GPS_TAG_DESTBEARING = 0X18 +GPS_TAG_DESTDISTANCEREF = 0X19 +GPS_TAG_DESTDISTANCE = 0X1A +GPS_TAG_PROCESSINGMETHOD = 0X1B +GPS_TAG_AREAINFORMATION = 0X1C +GPS_TAG_DATESTAMP = 0X1D +GPS_TAG_DIFFERENTIAL = 0X1E + +GpsTagTable = {} +GpsTagTable[GPS_TAG_VERSIONID] = "VersionID" +GpsTagTable[GPS_TAG_LATITUDEREF] = "LatitudeRef" +GpsTagTable[GPS_TAG_LATITUDE] = "Latitude" +GpsTagTable[GPS_TAG_LONGITUDEREF] = "LongitudeRef" +GpsTagTable[GPS_TAG_LONGITUDE] = "Longitude" +GpsTagTable[GPS_TAG_ALTITUDEREF] = "AltitudeRef" +GpsTagTable[GPS_TAG_ALTITUDE] = "Altitude" +GpsTagTable[GPS_TAG_TIMESTAMP] = "Timestamp" +GpsTagTable[GPS_TAG_SATELLITES] = "Satellites" +GpsTagTable[GPS_TAG_STATUS] = "Status" +GpsTagTable[GPS_TAG_MEASUREMODE] = "MeasureMode" +GpsTagTable[GPS_TAG_DOP] = "Dop" +GpsTagTable[GPS_TAG_SPEEDREF] = "SpeedRef" +GpsTagTable[GPS_TAG_SPEED] = "Speed" +GpsTagTable[GPS_TAG_TRACKREF] = "TrafRef" +GpsTagTable[GPS_TAG_TRACK] = "Track" +GpsTagTable[GPS_TAG_IMGDIRECTIONREF] = "ImgDirectionRef" +GpsTagTable[GPS_TAG_IMGDIRECTION] = "ImgDirection" +GpsTagTable[GPS_TAG_MAPDATUM] = "MapDatum" +GpsTagTable[GPS_TAG_DESTLATITUDEREF] = "DestLatitudeRef" +GpsTagTable[GPS_TAG_DESTLATITUDE] = "DestLatitude" +GpsTagTable[GPS_TAG_DESTLONGITUDEREF]= "DestLongitudeRef" +GpsTagTable[GPS_TAG_DESTLONGITUDE] = "DestLongitude" +GpsTagTable[GPS_TAG_DESTBEARINGREF] = "DestBearingref" +GpsTagTable[GPS_TAG_DESTBEARING] = "DestBearing" +GpsTagTable[GPS_TAG_DESTDISTANCEREF] = "DestDistanceRef" +GpsTagTable[GPS_TAG_DESTDISTANCE] = "DestDistance" +GpsTagTable[GPS_TAG_PROCESSINGMETHOD]= "ProcessingMethod" +GpsTagTable[GPS_TAG_AREAINFORMATION] = "AreaInformation" +GpsTagTable[GPS_TAG_DATESTAMP] = "Datestamp" +GpsTagTable[GPS_TAG_DIFFERENTIAL] = "Differential" + +FMT_BYTE = 1 +FMT_STRING = 2 +FMT_USHORT = 3 +FMT_ULONG = 4 +FMT_URATIONAL = 5 +FMT_SBYTE = 6 +FMT_UNDEFINED = 7 +FMT_SSHORT = 8 +FMT_SLONG = 9 +FMT_SRATIONAL = 10 +FMT_SINGLE = 11 +FMT_DOUBLE = 12 + +bytes_per_format = {0,1,1,2,4,8,1,1,2,4,8,4,8} + +portrule = shortport.http + +---Unpack a rational number from exif. In exif, a rational number is stored +--as a pair of integers - the numerator and the denominator. +-- +--@return the new position, and the value. +local function unpack_rational(endian, data, pos) + local v1, v2 + v1, v2, pos = string.unpack(endian .. "I4I4", data, pos) + return pos, v1 / v2 +end + +local function process_gps(data, pos, endian, result) + local value, num_entries + local latitude, latitude_ref, longitude, longitude_ref + + -- The first entry in the gps section is a 16-bit size + num_entries, pos = string.unpack(endian .. "I2", data, pos) + + -- Loop through the entries to find the fun stuff + for i=1, num_entries do + local tag, format, components, value + tag, format, components, value, pos = string.unpack(endian .. "I2 I2 I4 I4", data, pos) + + if(tag == GPS_TAG_LATITUDE or tag == GPS_TAG_LONGITUDE) then + local dummy, gps, h, m, s + dummy, h = unpack_rational(endian, data, value + 8) + dummy, m = unpack_rational(endian, data, dummy) + dummy, s = unpack_rational(endian, data, dummy) + + gps = h + (m / 60) + (s / 60 / 60) + + if(tag == GPS_TAG_LATITUDE) then + latitude = gps + else + longitude = gps + end + elseif(tag == GPS_TAG_LATITUDEREF) then + -- Get the first byte in the latitude reference as a character + latitude_ref = string.char(value >> 24) + elseif(tag == GPS_TAG_LONGITUDEREF) then + -- Get the first byte in the longitude reference as a character + longitude_ref = string.char(value >> 24) + end + end + + if(latitude and longitude) then + -- Normalize the N/S/E/W to positive and negative + if(latitude_ref == 'S') then + latitude = -latitude + end + if(longitude_ref == 'W') then + longitude = -longitude + end + + table.insert(result, string.format("GPS: %f,%f - https://maps.google.com/maps?q=%s,%s", latitude, longitude, latitude, longitude)) + end + + return true, result +end + +---Parse the exif data section and return a table. This has only been tested +--in a .jpeg file, but should work for .tiff as well. +local function parse_exif(exif_data) + local sig, marker, size + local tag, format, components, byte_count, value, offset, dummy, data + local status, result + local tiff_header_1, first_offset + + -- Initialize the result table + result = {} + + -- Read the verify the EXIF header + local header, endian, pos = string.unpack(">c6 I2", exif_data, 1) + if(header ~= "Exif\0\0") then + return false, "Invalid EXIF header" + end + + -- Check the endianness - it should only ever be big endian, but it doesn't + -- hurt to check + if(endian == 0x4d4d) then + endian = ">" + elseif(endian == 0x4949) then + endian = "<" + else + return false, "Unrecognized endianness entry" + end + + -- Read the first tiff header and the offset to the first data entry (should be 8) + tiff_header_1, first_offset, pos = string.unpack(endian .. "I2 I4", exif_data, pos) + if(tiff_header_1 ~= 0x002A or first_offset ~= 0x00000008) then + return false, "Invalid tiff header" + end + + -- Skip over the header, and go to the first offset (subtracting 1 because lua) + pos = first_offset + 8 - 1 + + -- The first 16-bit value is the number of entries + local num_entries, pos = string.unpack(endian .. "I2", exif_data, pos) + + -- Loop through the entries + for i=1,num_entries do + -- Read the entry's header + tag, format, components, value, pos = string.unpack(endian .. "I2 I2 I4 I4", exif_data, pos) + + -- Look at the tags we care about + if(tag == TAG_GPSINFO) then + -- If it's a GPSINFO tag, we need to parse the GPS structure + status, result = process_gps(exif_data, value + 8 - 1, endian, result) + if(not(status)) then + return false, result + end + else + value = string.unpack("z", exif_data, value + 8 - 1) + if (tag == TAG_MAKE) then + table.insert(result, string.format("Make: %s", value)) + elseif(tag == TAG_MODEL) then + table.insert(result, string.format("Model: %s", value)) + elseif(tag == TAG_DATETIME) then + table.insert(result, string.format("Date: %s", value)) + end + end + end + + return true, result +end + +---Parse a jpeg and find the EXIF data section +local function parse_jpeg(s) + local pos, sig, marker, size, exif_data + + -- Parse the jpeg header, make sure it's valid (we expect 0xFFD8) + sig, pos = string.unpack(">I2", s, pos) + if(sig ~= 0xFFD8) then + return false, "Unexpected signature" + end + + -- Parse the sections to find the exif marker (0xffe1) + while(true) do + marker, size, pos = string.unpack(">I2I2", s, pos) + + -- Check if we found the exif metadata section, break if we did + if(marker == 0xffe1) then + break + -- If the marker is nil, we're off the end of the image (and therefore, it wasn't found) + elseif(not(marker)) then + return false, "Could not found EXIF marker" + end + + -- Go to the next section (we subtract 2 because of the 2-byte marker we read) + pos = pos + size - 2 + end + + exif_data, pos = string.unpack(string.format(">c%d", size), s, pos) + + return parse_exif(exif_data) +end + + +function action(host, port) + local pattern = "%.jpg" + local images = {} + local results = {} + + -- once we know the pattern we'll be searching for, we can set up the function + local whitelist = function(url) + return string.match(url.file, "%.jpg") or string.match(url.file, "%.jpeg") + end + + local crawler = httpspider.Crawler:new( host, port, nil, { scriptname = SCRIPT_NAME, whitelist = { whitelist }} ) + + if ( not(crawler) ) then + return + end + + while(true) do + -- Begin the crawler + local status, r = crawler:crawl() + + -- Make sure there's no error + if ( not(status) ) then + if ( r.err ) then + return stdnse.format_output(false, r.reason) + else + break + end + end + + -- Check if we got a response, and the response is a .jpg file + if r.response and r.response.body and r.response.status==200 and (string.match(r.url.path, ".jpg") or string.match(r.url.path, ".jpeg")) then + local status, result + stdnse.debug1("Attempting to read exif data from %s", r.url.raw) + status, result = parse_jpeg(r.response.body) + if(not(status)) then + stdnse.debug1("Couldn't read exif from %s: %s", r.url.raw, result) + else + -- If there are any exif results, add them to the result + if(result and #result > 0) then + result['name'] = r.url.raw + table.insert(results, result) + end + end + end + end + + return stdnse.format_output(true, results) +end + -- cgit v1.2.3