You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1077 lines
34 KiB

#!/usr/bin/env bash
# Name: SearchSploit - Exploit-DB's CLI search tool
# Version: 4.1.3 (2020-06-22)
# Written by: Offensive Security, Unix-Ninja, and g0tmi1k
# Homepage: https://github.com/offensive-security/exploitdb
# Manual: https://www.exploit-db.com/searchsploit
#
## NOTE:
# Exit code '0' means finished normally
# Exit code '1' means something went wrong
# Exit code '2' means help screen
# Exit code '6' means updated packages (APT, brew or Git)
#-----------------------------------------------------------------------------#
## Settings File
rc_file=""
## Default options
CLIPBOARD=0
COLOUR=1
EDBID=0
EXACT=0
EXAMINE=0
FILEPATH=1
FUZZY=1
GETPATH=0
JSON=0
MIRROR=0
OVERFLOW=0
SCASE=0
VERBOSE=0
WEBLINK=0
XML=0
COLOUR_TAG=""
TAGS=""
SEARCH=""
EXCLUDE=""
CASE_TAG_GREP="-i"
CASE_TAG_FGREP="tolower"
AWK_SEARCH=""
FUZZY_SEARCH=""
VERSION=
COLOUR_OFF_GREP=
COLOUR_ON_GREP=
REGEX_GREP=
## Check if our grep supports --color
if grep --help 2>&1 | grep "[-]-color" >/dev/null 2>&1 ; then
COLOUR_OFF_GREP="--color=never"
COLOUR_ON_GREP="--color=always"
fi
## Check if our grep supports ---perl-regexp
if grep --help 2>&1 | grep "[-]-perl-regexp" >/dev/null 2>&1 ; then
REGEX_GREP="-P"
else
REGEX_GREP="-E"
fi
## Set LANG variable to avoid illegal byte sequence errors
LANG=C
## Usage info ~ https://www.tldp.org/LDP/abs/html/standard-options.html
function usage() {
echo " Usage: ${progname} [options] term1 [term2] ... [termN]"
echo ""
echo "=========="
echo " Examples "
echo "=========="
echo " ${progname} afd windows local"
echo " ${progname} -t oracle windows"
echo " ${progname} -p 39446"
echo " ${progname} linux kernel 3.2 --exclude=\"(PoC)|/dos/\""
echo " ${progname} -s Apache Struts 2.0.0"
echo " ${progname} linux reverse password"
echo " ${progname} -j 55555 | json_pp"
echo ""
echo " For more examples, see the manual: https://www.exploit-db.com/searchsploit"
echo ""
echo "========="
echo " Options "
echo "========="
echo "## Search Terms"
echo " -c, --case [Term] Perform a case-sensitive search (Default is inSEnsITiVe)"
echo " -e, --exact [Term] Perform an EXACT & order match on exploit title (Default is an AND match on each term) [Implies \"-t\"]"
echo " e.g. \"WordPress 4.1\" would not be detect \"WordPress Core 4.1\")"
echo " -s, --strict Perform a strict search, so input values must exist, disabling fuzzy search for version range"
echo " e.g. \"1.1\" would not be detected in \"1.0 < 1.3\")"
echo " -t, --title [Term] Search JUST the exploit title (Default is title AND the file's path)"
echo " --exclude=\"term\" Remove values from results. By using \"|\" to separate, you can chain multiple values"
echo " e.g. --exclude=\"term1|term2|term3\""
echo ""
echo "## Output"
echo " -j, --json [Term] Show result in JSON format"
echo " -o, --overflow [Term] Exploit titles are allowed to overflow their columns"
echo " -p, --path [EDB-ID] Show the full path to an exploit (and also copies the path to the clipboard if possible)"
echo " -v, --verbose Display more information in output"
echo " -w, --www [Term] Show URLs to Exploit-DB.com rather than the local path"
echo " --id Display the EDB-ID value rather than local path"
echo " --colour Disable colour highlighting in search results"
echo ""
echo "## Non-Searching"
echo " -m, --mirror [EDB-ID] Mirror (aka copies) an exploit to the current working directory"
echo " -x, --examine [EDB-ID] Examine (aka opens) the exploit using \$PAGER"
echo ""
echo "## Non-Searching"
echo " -h, --help Show this help screen"
echo " -u, --update Check for and install any exploitdb package updates (brew, deb & git)"
echo ""
echo "## Automation"
echo " --nmap [file.xml] Checks all results in Nmap's XML output with service version"
echo " e.g.: nmap [host] -sV -oX file.xml"
echo ""
echo "======="
echo " Notes "
echo "======="
echo " * You can use any number of search terms"
echo " * By default, search terms are not case-sensitive, ordering is irrelevant, and will search between version ranges"
echo " * Use '-c' if you wish to reduce results by case-sensitive searching"
echo " * And/Or '-e' if you wish to filter results by using an exact match"
echo " * And/Or '-s' if you wish to look for an exact version match"
echo " * Use '-t' to exclude the file's path to filter the search results"
echo " * Remove false positives (especially when searching using numbers - i.e. versions)"
echo " * When using '--nmap', adding '-v' (verbose), it will search for even more combinations"
echo " * When updating or displaying help, search terms will be ignored"
echo ""
exit 2
}
## Update database check
function update() {
arraylength="${#files_array[@]}"
for (( i=0; i<${arraylength}; i++ )); do
## Check to see if we already have the value
[[ "${tmp_package[*]}" =~ "${package_array[${i}]}" ]] \
&& continue
## Else save all the information
tmp_git+=("${git_array[${i}]}")
tmp_path+=("${path_array[${i}]}")
tmp_package+=("${package_array[${i}]}")
done
## Loop around all the new arrays
arraylength="${#tmp_git[@]}"
for (( i=0; i<${arraylength}; i++ )); do
git="${tmp_git[${i}]}"
path="${tmp_path[${i}]}"
package="${tmp_package[${i}]}"
## Update from the repos (e.g. Kali)
apt=$( apt-cache search "^${package}$" 2>/dev/null ) #dpkg -l "${package}" 2>/dev/null >/dev/null
if [[ "$?" == "0" ]] && [[ "${apt}" != "" ]]; then
updatedeb "${package}"
else
## Update from homebrew (e.g. OSX)
brew 2>/dev/null >/dev/null
if [[ "$?" == "0" ]]; then
## This only really only updates "./searchsploit". The rest (can) come via git as its updated more frequently
updatedbrew "${package}"
fi
## Update via Git
updategit "${package}" "${path}" "${git}"
fi
done
## Done
exit 6
}
## Update database (via .deb/apt)
function updatedeb() {
package_in="${1}"
echo -e "[i] Updating via apt package management (Expect weekly-ish updates): ${package_in}\n"
sudo apt update \
|| echo -e "\n[-] Issue with apt update (Please check network connectivity & apt SourcesList values)" 1>&2
sudo apt -y install "${package_in}" \
|| echo -e "\n[-] Issue with apt upgrade" 1>&2
echo -e "\n[*] apt update finished"
}
## Update database (via homebrew)
function updatedbrew() {
package_in="${1}"
echo -e "[i] Updating via brew package management\n"
brew update \
|| echo -e "\n[-] Issue with brew update (Please check network connectivity)" 1>&2
brew upgrade "${package_in}"
echo -e "\n[*] Brew update finished"
}
## Update database (via Git)
function updategit() {
package_in="${1}"
path_in="${2}"
git_in="${3}"
echo -e "[i] Updating via Git (Expect daily updates): ${package_in} ~ ${path_in}\n"
## Make sure we are in the correct folder
mkdir -p "${path_in}/" 2>/dev/null \
|| sudo mkdir -p "${path_in}/"
cd "${path_in}/"
## Are we in a Git repo?
if [[ "$( git rev-parse --is-inside-work-tree 2>/dev/null )" != "true" ]]; then
if [[ "$( ls )" = "" ]]; then
# If directory is empty, just clone
echo -e "\n[-] Nothing here (${path_in}). Starting fresh..."
git clone -v "${git_in}" "${path_in}/" 2>/dev/null \
|| sudo git clone -v "${git_in}" "${path_in}/"
fi
fi
# Is our Git remote added? (aka wouldn't be via homebrew method)
if [[ "$( git remote -v )" != *"upstream"*"${git_in}"* ]]; then
echo -e "\n[-] Missing Git remote upstream (${git_in})"
git init 2>/dev/null \
|| sudo git init
git remote add upstream "${git_in}" 2>/dev/null \
|| sudo git remote add upstream "${git_in}"
fi
# Make sure to prep checkout first
git checkout -- . 2>/dev/null \
|| sudo git checkout -- .
# Update from git
echo -e "\n[i] Git pull'ing"
git pull -v upstream master 2>/dev/null \
|| sudo git pull -v upstream master
# If conflicts, clean and try again
if [[ "$?" -ne 0 ]]; then
echo -e "\n[-] Git conflict"
git clean -d -fx "" \
|| sudo git clean -d -fx ""
git pull -v upstream master \
|| sudo git pull -v upstream master
fi
echo -e "\n[*] Git update finished"
echo "[i] Path: ${path_in}/"
}
## Printing dotted lines in the correct manner
function drawline() {
printf "%0.s-" $( eval echo {1..$(( COL1 + 1 ))} )
echo -n " "
printf "%0.s-" $( eval echo {1..$(( COL2 - 1 ))} )
echo ""
}
## Used in searchsploitout/nmap's XML
function validterm() {
## Check to see if its any phrases which would give a TON of incorrect results
if [ "$( echo ${1} | tr '[:upper:]' '[:lower:]' )" == "microsoft" ] \
|| [ "$( echo ${1} | tr '[:upper:]' '[:lower:]' )" == "microsoft windows" ] \
|| [ "$( echo ${1} | tr '[:upper:]' '[:lower:]' )" == "windows" ] \
|| [ "$( echo ${1} | tr '[:upper:]' '[:lower:]' )" == "apache" ] \
|| [ "$( echo ${1} | tr '[:upper:]' '[:lower:]' )" == "ftp" ] \
|| [ "$( echo ${1} | tr '[:upper:]' '[:lower:]' )" == "http" ] \
|| [ "$( echo ${1} | tr '[:upper:]' '[:lower:]' )" == "linux" ] \
|| [ "$( echo ${1} | tr '[:upper:]' '[:lower:]' )" == "net" ] \
|| [ "$( echo ${1} | tr '[:upper:]' '[:lower:]' )" == "network" ] \
|| [ "$( echo ${1} | tr '[:upper:]' '[:lower:]' )" == "oracle" ] \
|| [ "$( echo ${1} | tr '[:upper:]' '[:lower:]' )" == "ssh" ] \
|| [ "$( echo ${1} | tr '[:upper:]' '[:lower:]' )" == "unknown" ]; then
echo -e "[-] Skipping term: ${1} (Term is too general. Please re-search manually: $0 ${arg} ${1})\n" 1>&2
## Issues, return with something
return 1
fi
## No issues, return without anything
return 0
}
## Used in searchsploitout/nmap's XML
function searchsploitout() {
## Make sure there is a value
[ "${software}" = "" ] \
&& return
#echo "" 1>&2
arg="-t" ## Title search by default!
[[ "${COLOUR}" != "1" ]] \
&& arg="${arg} --colour"
[[ "${EDBID}" == "1" ]] \
&& arg="${arg} --id"
[[ "${JSON}" == "1" ]] \
&& arg="${arg} --json"
[[ "${OVERFLOW}" == "1" ]] \
&& arg="${arg} --overflow"
[[ "${FUZZY}" != "1" ]] \
&& arg="${arg} --strict"
[[ "${WEBLINK}" == "1" ]] \
&& arg="${arg} --www"
## Try and remove terms that could confuse searches
#software=$( echo "${software}" | sed 's_/_ _g' )
software=$( echo "${software}" | sed -e 's/[^a-zA-Z0-9]/ /g' )
if [[ "${VERBOSE}" -eq 1 ]]; then
## Loop each word?
tmp=""
for word in $( echo ${software} ); do
## Add current search term on
tmp="${tmp}${word}"
## Check to see if its any phrases which would give a TON of incorrect results
validterm "${tmp}" \
|| continue
## Feedback
echo "[i] $0 ${arg} ${tmp}" 1>&2
out=$( bash "$0" ${arg} ${tmp} )
## Are there too many results?
lines=$( echo -e "${out}" | wc -l )
if [[ "${lines}" -gt 100 ]]; then
echo -e "[-] Skipping output: ${tmp} (Too many results, 100+. You'll need to force a search: $0 ${arg} ${tmp})\n" 1>&2
## Are there any results?
elif [[ "${lines}" -gt 5 ]]; then
echo -e "${out}\n\n"
## If there's no results
else
## Exit for loop
break
fi
## Space out for the next word
tmp="${tmp} "
done
## Padding between loops
echo -e "\n\n" 1>&2
else
## Check to see if its any phrases which would give a TON of incorrect results
validterm "${software}" \
|| return
## Feedback
echo "[i] $0 ${arg} ${software}" 1>&2
out=$( bash "$0" ${arg} ${software} )
## Are there too many results?
lines=$( echo -e "${out}" | wc -l )
if [[ "${lines}" -gt 100 ]]; then
echo -e "[-] Skipping output: ${software} (Too many results, 100+. You'll need to force a search: $0 ${arg} ${software})\n" 1>&2
## Are there any results?
elif [[ "${lines}" -gt 5 ]]; then
echo -e "${out}\n\n"
fi
fi
}
## Read XML file
function nmapxml() {
## Feedback to the end user
echo -e "[i] Reading: '${FILE}'\n" 1>&2
## Read in XMP (IP, name, service and version)
xmllint --xpath '//address/@addr|//service/@name|//service/@product|//service/@version' "${FILE}" \
| sed -e $'s/addr=/\\\n[IP] /g; s/name=/\\\n[NAME] /g; s/product=/\\\n[PRODUCT] /g;s/" version="/\\\n[VERSION] /g; s/"//g' \
| grep -v '\[IP\].*\:' \
| while read line; do
type=$( echo "${line}" | cut -d" " -f 1 )
input=$( echo "${line}" | cut -d" " -f 2- )
case "${type}" in
"[IP]")
#[[ "${VERBOSE}" -eq 1 ]] && echo -e "\n\n\e[32m[*] IP: ${input}\e[39m" 1>&2
;;
"[NAME]")
## If we have already looped around and got something, save it before moving onto the current value
if [[ "${software}" ]]; then
#searchsploitout
echo "${software}"
fi
## Something is better than nothing. Will just go on the default service that matches the port. e.g. domain
software="${input}"
## Might not get any more than this, if -sV failed
;;
"[PRODUCT]")
## We have a name, but no version (yet?) e.g. dnsmasq
echo "${software}"
software="${input}"
echo "${software}"
;;
"[VERSION]")
software="${software} ${input}"
## Name & version. There isn't any more information to get, game over. e.g. dnsmasq 2.72
echo "${software}"
software=
;;
esac
done | tr '[:upper:]' '[:lower:]' | awk '!x[$0]++' | while read software; do
searchsploitout
done
}
## Build search terms
function buildterms() {
tag_in="${1}"
## If we are to use colour ("--colour"), add the values to search for between "or"
if [[ "${COLOUR}" -eq 1 ]]; then
[[ "${COLOUR_TAG}" ]] \
&& COLOUR_TAG="${COLOUR_TAG}|"
COLOUR_TAG="${COLOUR_TAG}${tag_in}"
fi
## Search both title AND path
if [[ "${FILEPATH}" -eq 1 ]]; then
## Search command for each term (with case sensitive flag, "-c")
SEARCH="${SEARCH} | grep ${COLOUR_OFF_GREP} -F ${CASE_TAG_GREP} \"${tag_in}\""
## Some regex to try and detect version
## Basic: major.minor[.build][.revision] // major.minor[.maintenance][.build] -- example: 1.2.3.4)
## Plus alphanumeric (e.g. alpha, beta): 1a, 2.2b, 3.3-c, 4.4-rc4, 5.5-r
if ! echo "${tag_in}" | grep ${REGEX_GREP} -q "^(\d+)(\.?\d*)(\.?\d*)((\.|\-)?(\w*))$"; then
FUZZY_SEARCH="${FUZZY_SEARCH} | grep ${COLOUR_OFF_GREP} -F ${CASE_TAG_GREP} \"${tag_in}\""
fi
## Search just the title, NOT the path ("-t"/"-e")
else
## If there is already a value, prepend text to get ready
[[ "${AWK_SEARCH}" ]] \
&& AWK_SEARCH="${AWK_SEARCH}/ && ${CASE_TAG_FGREP}(\$3) ~ /"
## Escape any slashes
tag_in="$( echo ${tag_in} | sed 's_/_\\/_g' )"
## Case sensitive ("-c")
if [[ "${SCASE}" -eq 1 ]]; then
AWK_SEARCH="${AWK_SEARCH}${tag_in}"
else
AWK_SEARCH="${AWK_SEARCH}$( echo ${tag_in} | tr '[:upper:]' '[:lower:]' )"
fi
fi
}
## Read in the values from files_*.csv
function findresults() {
file_in="${1}"
path_in="${2}"
name_in="${3}"
if [[ "${name_in}" == "Paper"* ]]; then
url="papers"
elif [[ "${name_in}" == "Shellcode"* ]]; then
url="shellcodes"
else
url="exploits"
fi
## JSON require full options ("--json")
if [[ "${JSON}" -eq 1 ]] || [[ "${FUZZY}" -eq 1 ]]; then
## Read (id, path, title, date, author, type, platform) separated between commas
## Needs to end with a `,` to match the awk search later for FUZZY_SEARCH with "sort -u"
SEARCH="awk -F '[,]' '{print \$1\",\"\$2\",\"\$3\",\"\$4\",\"\$5\",\"\$6\",\"\$7\",\"}' \"${path_in}/${file_in}\""
## Read (id, path, title) separated between commas & search for less than (and grater than values) too
FUZZY_SEARCH="awk -F '[,]' '{print \$1\",\"\$2\",\"\$3}' \"${path_in}/${file_in}\" | grep ${COLOUR_OFF_GREP} \"<\|>\""
else
## Read (id, path, title) separated between commas (as these are the only visible fields)
SEARCH="awk -F '[,]' '{print \$1\",\"\$2\",\"\$3}' \"${path_in}/${file_in}\""
fi
## EXACT search command ("-e")
if [[ "${EXACT}" -eq 1 ]]; then
buildterms "${TAGS}"
## or AND search command?
else
## For each term
for TAG in ${TAGS}; do
buildterms "${TAG}"
done
fi
## If we are NOT to use the path name ("-t"/"-e")
[[ "${FILEPATH}" -eq 0 ]] \
&& SEARCH="${SEARCH} | awk -F '[,]' '${CASE_TAG_FGREP}(\$3) ~ /${AWK_SEARCH}/ {print}'"
## Remove any terms not wanted from the search
[[ "${EXCLUDE}" ]] \
&& SEARCH="${SEARCH} | grep ${REGEX_GREP} -vi '${EXCLUDE}'"
[[ "${EXCLUDE}" ]] && [[ "${FUZZY}" -eq 1 ]] \
&& FUZZY_SEARCH="${FUZZY_SEARCH} | grep ${REGEX_GREP} -vi '${EXCLUDE}'"
## If we are to use colour ("--colour"), add the value here
if [[ "${COLOUR_TAG}" ]] && [[ "${JSON}" -eq 0 ]]; then
COLOUR_TAG="grep ${COLOUR_ON_GREP} -iE \"${COLOUR_TAG}|$\""
fi
## Dynamically set column widths to the current screen size
[[ "${WEBLINK}" -eq 1 ]] \
&& COL2=45 \
|| COL2=$(( 34 )) ## Max length + 2 ~ $ find . ! -path '*/.*' -type f | awk '{ print length, $0 }' | sort -n -s | cut -d" " -f2- | tail -n 1
#|| COL2=$(( ${#path_in} + 21 ))
COL1=$(( $( tput cols ) - COL2 - 1 ))
## Search, format, and print results (--overflow)
[[ "${OVERFLOW}" -eq 1 ]] \
&& FORMAT_COL1=${COL1} \
|| FORMAT_COL1=${COL1}'.'${COL1}
## Maximum length COL2 can be
FORMAT_COL2=$(( ${COL2} - 2 ))
## Are we doing a fuzzy search & did we manage to detect the version
if [[ "${FUZZY}" -eq 1 ]] && [[ -n "${VERSION}" ]]; then
## SubShells - http://mywiki.wooledge.org/BashFAQ/024
while IFS= read -r TITLE; do
while IFS= read -r RANGE; do
## Read in input and trim
MIN=$( echo "${RANGE}" | awk -F '<' '{print $1}' | xargs )
MAX=$( echo "${RANGE}" | awk -F '<' '{print $2}' | xargs )
## As its optional to put it, set a value if blank
[ -z "${MIN}" ] \
&& MIN=0
RESULT="$( printf '%s\n' "${MIN}" "${VERSION}" "${MAX}" | sort -V )"
## Sub if sort -v isn't working? if (( $( echo "${MIN} <= ${VERSION}" | bc -l ) )) && (( $( echo "${MAX} >= ${VERSION}" | bc -l ) )) ; then
## ...else there is dpkg (if Debian)
if [[ "$( echo "${RESULT}" | head -n 1 )" == "${MIN}" ]] \
&& [[ "$( echo "${RESULT}" | tail -n 1 )" == "${MAX}" ]]; then
[ -n "${ID}" ] \
&& ID="${ID}|"
ID="${ID}$( echo $TITLE | awk -F ',' '{print $1}' )"
## Found one, no point going on
break
fi
done < <(
echo "${TITLE}" \
| grep ${REGEX_GREP} -o "((\d+)(\.?\d*)(\.?\d*)((\.|\-)?(\d|x)*)(\s*))?((<|>)=?)(\s*)(\d+)(\.?\d*)(\.?\d*)((\.|\-)?(\d|x)*)" \
| sed 's_=__; s_>_<_'
)
## Do the same search (just without the version) & loop around all the exploit titles (as thats where the versions are)
## Two main "parts"
## (a.b.c.d )(<= e.f.g.h)
## This can be broken down more:
## Group 1 == a & e == major = [0-9]
## Group 2 == b & f == minor = .[0-9] (optional)
## Group 3 == c & g == build/maintenance = .[0-9] (optional)
## Group 4a == d & h == revision/build = . OR - (optional)
## Group 4b == = x OR [0-9] (optional)
## So it really is more like ~ (a)(.b)(.c)(.d)( )(<=)( )(e)(.f)(.g)(.h)
## NOTE: ..."x" is used as a wild card in titles
## Quick regex recap
## Digit == \d
## Space == \s
## Group == ( )
## OR == |
## 1 or more == +
## 0 or more == *
## 0 or 1 == ?
## Should support:
## Exploit < 1 / <= 1.2 / < 1.2.3.4 / < 1.2.3.x
## Exploit 1.0 < 1.2.3.4
## ...This can be better so it doesn't search in brackets: "Linux Kernel (Solaris 10 / < 5.10 138888-01) - Local Privilege Escalation"
done < <(
eval "${FUZZY_SEARCH}"
)
fi
## Magic search Fu + strip double quotes + Fix any escaping `\` (need todo it again for JSON only later: issues/#173)
OUTPUT="$(
( \
eval ${SEARCH}; \
awk "/^(${ID}),/ {print}" "${path_in}/${file_in}" \
) \
| sed 's/\"//g; s_\\_\\\\_g' \
| sort -u
)"
## If there are no results, no point going on
[[ -z "$OUTPUT" ]] \
&& return
## Print JSON format (full options) ("--json")?
if [[ "${JSON}" -eq 1 ]]; then
## Web link format ("--json --www")?
if [[ "${WEBLINK}" -eq 1 ]]; then
OUTPUT="$( echo "${OUTPUT}" \
| sed 's_\\_\\\\_g' \
| awk -F ',' '{ printf "\\n\\t\\t'{'\"Title\":\"%s\",\"URL\":\"https://www.exploit-db.com/'${url}'/%s\"},", $3, $1 }' )"
## Just the EDB-ID ("--json --id")?
elif [[ "${EDBID}" -eq 1 ]]; then
OUTPUT="$( echo "${OUTPUT}" \
| sed 's_\\_\\\\_g' \
| awk -F ',' '{ printf "\\n\\t\\t'{'\"Title\":\"%s\",\"EDB-ID\":\"%s\",\"Path\":\"'${path_in}/'%s\"},", $3, $1, $2 }' )"
## Default JSON ("--json")?
else
OUTPUT="$( echo "${OUTPUT}" \
| sed 's_\\_\\\\_g' \
| awk -F ',' '{ printf "\\n\\t\\t'{'\"Title\":\"%s\",\"EDB-ID\":\"%s\",\"Date\":\"%s\",\"Author\":\"%s\",\"Type\":\"%s\",\"Platform\":\"%s\",\"Path\":\"'${path_in}/'%s\"},", $3, $1, $4, $5, $6, $7, $2 }' )"
fi
OUTPUT="$( echo -e ${OUTPUT} \
| sort -f \
| sed '$ s/,$//' )"
## Web link format ("--www")?
elif [[ "${WEBLINK}" -eq 1 ]]; then
OUTPUT="$( echo "${OUTPUT}" \
| awk -F ',' '{ printf "%-'${FORMAT_COL1}'s | %s\n", $3, "https://www.exploit-db.com/'${url}'/"$1 }' \
| sort -f )"
## Just the EDB-ID ("--id")?
elif [[ "${EDBID}" -eq 1 ]]; then
OUTPUT="$( echo "${OUTPUT}" \
| awk -F ',' '{ printf "%-'${FORMAT_COL1}'s | %s\n", $3, $1 }' \
| sort -f )"
## Default view
else
OUTPUT="$( echo "${OUTPUT}" \
| sed 's_,exploits/_,_; s_,shellcodes/_,_; s_,papers/_,_' \
| awk -F ',' '{ printf "%-'${FORMAT_COL1}'s | %.'${FORMAT_COL2}'s\n", $3, $2 }' \
| sort -f )"
fi
## Display colour highlights ("--colour")?
if [[ "${COLOUR_TAG}" ]] && [[ "${JSON}" -eq 0 ]] && [[ "${OUTPUT}" ]]; then
OUTPUT=$( echo -e "${OUTPUT}" | eval ${COLOUR_TAG} )
fi
}
function printresults() {
title_in="${1}"
path_in="${2}"
json_title="$( echo ${title_in} | tr /a-z/ /A-Z/ )"
## Print header if in JSON ("--json")
if [[ "${JSON}" -eq 1 ]]; then
printf ",\n\t\"DB_PATH_${json_title}\": \"${path_in}\",\n"
printf "\t\"RESULTS_${json_title}\": ["
## ASCII table
else
drawline
printf "%-${COL1}s %s" " ${title_in} Title"
if [[ "${WEBLINK}" -eq 1 ]]; then
echo "| URL"
elif [[ "${EDBID}" -eq 1 ]]; then
echo "| EDB-ID"
else
echo "| Path"
#echo " > Results (0)"
#
#printf "%-${COL1}s "
#echo "| (${path_in}/)"
fi
drawline
fi
## Show content
[[ "${OUTPUT}" ]] \
&& echo "${OUTPUT}"
## Print footer if in JSON ("--json")
if [[ "${JSON}" -eq 1 ]]; then
printf "\t]"
else
drawline
fi
}
#-----------------------------------------------------------------------------#
## Locate setting file
## User home folder config
if [[ -f "${HOME}/.searchsploit_rc" ]]; then
rc_file="${HOME}/.searchsploit_rc"
## Global config
elif [[ -f "/etc/searchsploit_rc" ]]; then
rc_file="/etc/searchsploit_rc"
## Method #1 - File itself
elif [[ -f "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/.searchsploit_rc" ]]; then
rc_file="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/.searchsploit_rc"
## Method #2 - Symbolic link
elif [[ -f "$( dirname "$( readlink "$0" )" )/.searchsploit_rc" ]]; then
rc_file="$( dirname "$( readlink "$0" )" )/.searchsploit_rc"
## Manually specified?
elif [[ ! -f "${rc_file}" ]]; then
echo "[!] Could not find: rc_file ~ ${rc_file}"
exit 1
fi
## Use config file
source "${rc_file}"
#-----------------------------------------------------------------------------#
## Check for empty arguments
if [[ $# -eq 0 ]]; then
usage >&2
fi
## Parse long arguments
ARGS="-"
for param in "$@"; do
if [[ "${param}" == "--case" ]]; then
SCASE=1
elif [[ "${param}" == "--colour" ]] || [[ "${param}" == "--color" ]]; then
COLOUR=""
elif [[ "${param}" == "--exact" ]]; then
EXACT=1
elif [[ "${param}" == "--examine" ]] || [[ "${param}" == "--open" ]] || [[ "${param}" == "--view" ]]; then
GETPATH=1
EXAMINE=1
elif [[ "${param}" == "--help" ]]; then
usage >&2
elif [[ "${param}" == "--id" ]]; then
EDBID=1
elif [[ "${param}" == "--json" ]]; then
JSON=1
elif [[ "${param}" == "--mirror" ]] || [[ "${param}" == "--copy" ]] || [[ "${param}" == "--dup" ]] || [[ "${param}" == "--duplicate" ]]; then
GETPATH=1
MIRROR=1
elif [[ "${param}" == "--nmap" ]]; then
XML=1
elif [[ "${param}" == "--overflow" ]]; then
OVERFLOW=1
elif [[ "${param}" == "--path" ]]; then
GETPATH=1
CLIPBOARD=1
elif [[ "${param}" == "--strict" ]]; then
FUZZY=0
elif [[ "${param}" == "--title" ]]; then
FILEPATH=0
elif [[ "${param}" == "--update" ]]; then
update
elif [[ "${param}" == "--verbose" ]]; then
VERBOSE=1
elif [[ "${param}" == "--www" ]]; then
WEBLINK=1
elif [[ "${param}" =~ "--exclude=" ]]; then
EXCLUDE="$( echo "${param}" | cut -d '=' -f 2- )"
else
if [[ "${param:0:1}" == "-" ]]; then
ARGS=${ARGS}${param:1}
shift
continue
fi
TAGS="${TAGS} ${param//\`/_}"
fi
done
## Parse short arguments
while getopts "cehjmnopstuvwx" arg "${ARGS}"; do
if [[ "${arg}" = "?" ]]; then
usage >&2;
fi
case ${arg} in
c) SCASE=1;;
e) EXACT=1;;
h) usage >&2;;
j) JSON=1;;
m) GETPATH=1; MIRROR=1;;
n) XML=1;;
o) OVERFLOW=1;;
p) GETPATH=1; CLIPBOARD=1;;
s) FUZZY=0;;
t) FILEPATH=0;;
u) update;;
v) VERBOSE=1;;
w) WEBLINK=1;;
x) GETPATH=1; EXAMINE=1;;
esac
shift $(( OPTIND - 1 ))
done
#-----------------------------------------------------------------------------#
## Check for files_*.csv
arraylength="${#files_array[@]}"
for (( i=0; i<${arraylength}; i++ )); do
files="${path_array[${i}]}/${files_array[${i}]}"
if [[ -f "${files}" ]]; then
continue
## Method #1 - File itself
elif [[ -f "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/${files_array[${i}]}" ]]; then
echo "[i] Found (#1): $( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/${files_array[${i}]}" 1>&2
echo "[i] To remove this message, please edit \"${rc_file}\" for \"${files_array[${i}]}\" (package_array: ${package_array[${i}]})" 1>&2
echo 1>&2
path_array[${i}]="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
## Method #2 - Symbolic link
elif [[ -f "$( dirname "$( readlink "$0" )" )/${files_array[${i}]}" ]]; then
echo "[i] Found (#2): $( dirname "$( readlink "$0" )" )/${files_array[${i}]}" 1>&2
echo "[i] To remove this message, please edit \"${rc_file}\" for \"${files_array[${i}]}\" (package_array: ${package_array[${i}]})" 1>&2
echo 1>&2
path_array[${i}]="$( dirname "$( readlink "$0" )" )"
else
#echo "[!] Could not find: ${files}" 1>&2
#echo "[i] To remove this message, please remove \"${files_array[${i}]}\" (package_array: ${package_array[${i}]}) from \"${rc_file}\"" 1>&2
#echo 1>&2
unset "files_array[${i}]"
unset "path_array[${i}]"
unset "name_array[${i}]"
unset "git_array[${i}]"
unset "package_array[${i}]"
fi
done
#-----------------------------------------------------------------------------#
## Read in XML
if [[ "${XML}" -eq 1 ]]; then
## Trim white spaces
FILE=$( echo ${TAGS} | xargs )
## Is there a file?
if [[ ! -f "${FILE}" ]]; then
echo -e "\n[!] Could not find file: ${FILE}" 1>&2
exit 1
fi
if ! hash xmllint 2>/dev/null; then
echo -e "\n[!] Please install xmllint" 1>&2
echo -e "[i] Kali Linux: sudo apt -y install libxml2-utils" 1>&2
exit 1
fi
if [[ "${VERBOSE}" -ne 1 ]]; then
echo "[i] SearchSploit's XML mode (without verbose enabled). To enable: ${progname} -v --xml..." 1>&2
fi
## Do the magic
nmapxml
## Done
exit 0
fi
## Print the full path. If pbcopy/xclip is available then copy to the clipboard
if [[ "${GETPATH}" -eq 1 ]]; then
for exploit in ${TAGS}; do
## Get EDB-ID from input
edbdb="$( echo ${exploit} | rev | cut -d '/' -f1 | rev | cut -d'-' -f1 | cut -d'.' -f1 | tr -dc '0-9' )"
## Loop until we find something
arraylength="${#files_array[@]}"
for (( i=0; i<${arraylength}; i++ )); do
files="${path_array[${i}]}/${files_array[${i}]}"
## Check to see if the files_*.csv has a value
line=$( grep -m 1 -E "^${edbdb}," "${files}" )
if [[ "${line}" ]]; then
path="$( echo $line | cut -d ',' -f 2 )"
location="${path_array[${i}]}/${path}"
name="${name_array[${i}]}"
if [[ "${name}" == "Paper"* ]]; then
url="papers/${edbdb}"
elif [[ "${name}" == "Shellcode"* ]]; then
url="shellcodes/${edbdb}"
else
url="exploits/${edbdb}"
fi
break
fi
done
## Did we find the exploit?
if [[ -f "${location}" ]]; then
## Get title
title=$( grep -m 1 "${path}" "${files}" | cut -d ',' -f 3 | sed 's/"//g' )
## File type
fileinfo="$( file -b "${location}" )"
## How long is the name?
PADDING=$(( 9 - ${#name} ))
## Display out
printf "%-${PADDING}s%s"
echo "${name}: ${title}"
echo " URL: https://www.exploit-db.com/${url}"
echo " Path: ${location}"
echo "File Type: ${fileinfo}"
echo ""
## Copy to clipboard?
if [[ "${CLIPBOARD}" -eq 1 ]]; then
## Are any copy programs available?
if hash xclip 2>/dev/null || hash pbcopy 2>/dev/null; then
## Linux (Will require $DISPLAY)
if hash xclip 2>/dev/null; then
echo -ne "${location}" | xclip -selection clipboard 2>/dev/null
echo "Copied EDB-ID #${edbdb}'s path to the clipboard"
## OSX
elif hash pbcopy 2>/dev/null; then
echo -ne "${location}" | pbcopy
echo "Copied EDB-ID #${edbdb}'s path to the clipboard"
fi
fi
## Done (early!)
exit 0
fi
## Open the exploit up?
if [[ "${EXAMINE}" -eq 1 ]]; then
if [[ "${PAGER}" ]]; then
/bin/sh -c "${PAGER} ${location}"
elif [[ -f "$( which pager 2>/dev/null )" ]]; then
pager "${location}"
else
less "${location}"
fi
echo -e "\n"
fi
if [[ "${MIRROR}" -eq 1 ]]; then
cp -i "${location}" "$( pwd )/"
echo "Copied to: $( pwd )/$( basename ${location} )"
echo -e "\n"
fi
else
## Feedback
echo "[!] Could not find EDB-ID #${edbdb}"
echo -e "\n"
fi
done
## Done
exit 0
fi
#-----------------------------------------------------------------------------#
## Are we are doing an exact match ("-e")? If so, do NOT check folder path (Implies "-t").
[[ "${EXACT}" -eq 1 ]] \
&& FILEPATH=0
## Case sensitive ("-c"), remove the default flags
[[ "${SCASE}" -eq 1 ]] \
&& CASE_TAG_GREP="" \
&& CASE_TAG_FGREP=""
## Remove leading space
TAGS="$( echo ${TAGS} | sed -e 's/^[[:space:]]//' )"
## Check to see if the version of "sort" is supported
echo | sort -V 2>/dev/null >/dev/null
if [ $? -ne "0" ]; then
echo "[-] 'sort' doesn't support '-V'" 1>&2
echo "[i] Enabling '${progname} --strict'" 1>&2
FUZZY=0
fi
## Some regex to try and detect version
## Basic: major.minor[.build][.revision] // major.minor[.maintenance][.build] -- example: 1.2.3.4)
## Plus alphanumeric (e.g. alpha, beta): 1a, 2.2b, 3.3-c, 4.4-rc4, 5.5-r
for tag_in in ${TAGS}; do
if echo "${tag_in}" | grep ${REGEX_GREP} -q "^(\d+)(\.?\d*)(\.?\d*)((\.|\-)?(\w*))$"; then
## 1.2.3-4abc
VERSION=$( echo "${tag_in}" | grep ${REGEX_GREP} -o "^(\d+)(\.?\d*)(\.?\d*)((\.|\-)?(\w*))$" )
[[ -n "${VERSION}" ]] && [[ "${VERBOSE}" -eq 1 ]] \
&& echo "[i] Version ID: ${VERSION}"
## 1.2.3-4
CLEANVERSION=$( echo "${tag_in}" | grep ${REGEX_GREP} -o "^(\d*\.?)(\d*\.?)(\d*\.?)((\.|\-)?(\d+))" )
if [[ -n "${CLEANVERSION}" ]] && [[ "${CLEANVERSION}" != "${VERSION}" ]]; then
VERSION="${CLEANVERSION}"
[[ "${VERBOSE}" -eq 1 ]] \
&& echo "[i] Clean ID: ${VERSION}"
fi
fi
done
## Did not get a version? If so, no point doing a fuzzy search
if [[ "${FUZZY}" -eq 1 ]] && [[ -z "${VERSION}" ]] && [[ "${VERBOSE}" -eq 1 ]]; then
echo "[i] Unable to detect version in terms: ${TAGS}" 1>&2
echo "[i] Enabling '${progname} --strict'" 1>&2
FUZZY=0
fi
## Is it just a single tag, disable fuzzy
[[ "${TAGS}" != *" "* ]] \
&& FUZZY=0
#-----------------------------------------------------------------------------#
## Print header if in JSON ("--json")
[[ "${JSON}" -eq 1 ]] \
&& printf "{\n\t\"SEARCH\": \"${TAGS}\""
## Check for files_*.csv
arraylength="${#files_array[@]}"
for (( i=0; i<${arraylength}; i++ )); do
## Search
findresults "${files_array[${i}]}" "${path_array[${i}]}" "${name_array[${i}]}"
## Print results if in JSON ("--json") or if there are any results
if ([[ "${JSON}" -eq 1 ]] || [[ "${OUTPUT}" ]]); then
printresults "${name_array[${i}]}" "${path_array[${i}]}"
## Summary if NOT JSON ("--json")
elif [[ "${JSON}" -eq 0 ]]; then
echo "${name_array[${i}]}s: No Results"
fi
## Reset
COLOUR_TAG=""
done
## Print footer if in JSON ("--json")
[[ "${JSON}" -eq 1 ]] \
&& printf "\n}\n"
## Done
exit 0