mirror of
https://github.com/nmap/nmap.git
synced 2025-12-06 04:31:29 +00:00
for lib in nselib/*.lua*; do l=${lib#*/}; l=${l%.lua*}; find . -name \
\*.lua -o -name \*.nse | xargs grep -l "require .$l\>" | xargs grep \
-c "\<$l\." | grep ':0$' | awk -F: '{print "'$l'", $1}'; done
Did not remove calls to stdnse.silent_require since these can be used to
abort script execution if OpenSSL is not included, even if the script
does not directly call openssl.* (perhaps it uses comm.tryssl instead,
for instance).
Also did not remove require "strict", since that library is special and
modifies the environment.
541 lines
20 KiB
Lua
541 lines
20 KiB
Lua
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 <host>
|
|
--
|
|
-- @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 http://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 bin = require 'bin'
|
|
local bit = require 'bit'
|
|
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
|
|
pos, v1, v2 = bin.unpack(endian .. "II", 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
|
|
pos, num_entries = bin.unpack(endian .. "S", data, pos)
|
|
|
|
-- Loop through the entries to find the fun stuff
|
|
for i=1, num_entries do
|
|
local pos, tag, format, components, value = bin.unpack(endian .. "SSII", 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(bit.rshift(value, 24))
|
|
elseif(tag == GPS_TAG_LONGITUDEREF) then
|
|
-- Get the first byte in the longitude reference as a character
|
|
longitude_ref = string.char(bit.rshift(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 pos, header1, header2, endian = bin.unpack(">ISS", exif_data, 1)
|
|
if(header1 ~= 0x45786966 or header2 ~= 0x0000) 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)
|
|
pos, tiff_header_1, first_offset = bin.unpack(endian .. "SI", 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 pos, num_entries = bin.unpack(endian .. "S", exif_data, pos)
|
|
|
|
-- Loop through the entries
|
|
for i=1,num_entries do
|
|
-- Read the entry's header
|
|
pos, tag, format, components, value = bin.unpack(endian .. "SSII", 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
|
|
elseif(tag == TAG_MAKE) then
|
|
dummy, value = bin.unpack("z", exif_data, value + 8 - 1)
|
|
table.insert(result, string.format("Make: %s", value))
|
|
elseif(tag == TAG_MODEL) then
|
|
dummy, value = bin.unpack("z", exif_data, value + 8 - 1)
|
|
table.insert(result, string.format("Model: %s", value))
|
|
elseif(tag == TAG_DATETIME) then
|
|
dummy, value = bin.unpack("z", exif_data, value + 8 - 1)
|
|
table.insert(result, string.format("Date: %s", value))
|
|
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)
|
|
pos, sig = bin.unpack(">S", s, pos)
|
|
if(sig ~= 0xFFD8) then
|
|
return false, "Unexpected signature"
|
|
end
|
|
|
|
-- Parse the sections to find the exif marker (0xffe1)
|
|
while(true) do
|
|
pos, marker, size = bin.unpack(">SS", 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
|
|
|
|
pos, exif_data = bin.unpack(string.format(">A%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.print_debug(1, "Attempting to read exif data from %s", r.url.raw)
|
|
status, result = parse_jpeg(r.response.body)
|
|
if(not(status)) then
|
|
stdnse.print_debug(1, "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
|
|
|