#!/usr/bin/env bash # ---------------------------------------------------------------------- # Copyright (c) 2024 Pablo Caro. All Rights Reserved. # Pablo Caro - https://pcaro.es/ # BaconHash client # ---------------------------------------------------------------------- set -euo pipefail API_ENDPOINT="https://baconhash.pw/api" TOKEN_FILE="$HOME/.baconhash" # ----------------------------------------------------------------------- # Resolve token: env var > token file # ----------------------------------------------------------------------- TOKEN="${BACONHASH_TOKEN:-}" if [[ -z "$TOKEN" && -f "$TOKEN_FILE" ]]; then TOKEN=$(head -n 1 "$TOKEN_FILE") fi if [[ -z "$TOKEN" ]]; then echo "ERROR: No API token found." >&2 echo " Set BACONHASH_TOKEN env variable, or store your token in $TOKEN_FILE:" >&2 echo " echo '' > $TOKEN_FILE" >&2 exit 1 fi # ----------------------------------------------------------------------- # Usage # ----------------------------------------------------------------------- function usage() { cat < $(basename "$0") $(basename "$0") upload $(basename "$0") upload DESCRIPTION: Look up MD5, NTLM, or SHA1 hashes against the BaconHash database. HASH LOOKUP: When a FILE is provided (one hash per line), the hashes are sent as a bulk POST request and results are printed as JSON. When a single HASH is provided, a GET request is made and the result is printed as JSON. UPLOAD PASSWORDS: Upload plaintext passwords so their hashes become searchable. When a FILE is provided (one password per line), all passwords are uploaded in a single request. When a single PASSWORD is provided, it is uploaded directly. TOKEN: Store your token in $TOKEN_FILE (one line), or export BACONHASH_TOKEN=. EXAMPLES: $(basename "$0") 2707569be0aff4a956388a851c68b4c6 $(basename "$0") hashes.txt $(basename "$0") upload "plaintext-password" $(basename "$0") upload passwords.txt EOF } # ----------------------------------------------------------------------- # Pretty-print helper (jq if available, else raw) # ----------------------------------------------------------------------- function pretty() { if command -v jq &>/dev/null; then jq . else cat fi } if [[ $# -ne 1 && $# -ne 2 ]]; then usage exit 0 fi # ----------------------------------------------------------------------- # Dispatch # ----------------------------------------------------------------------- if [[ "$1" == "upload" ]]; then # Upload subcommand if [[ $# -ne 2 ]]; then echo "ERROR: upload requires exactly one argument (password or file)" >&2 usage exit 1 fi ARG="$2" if [[ -f "$ARG" ]]; then # File with passwords: read lines and build JSON array JSON_BODY=$(jq -Rn '[inputs | select(length > 0)]' < "$ARG" 2>/dev/null \ || python3 -c " import sys, json lines = [l.strip() for l in open('$ARG') if l.strip()] print(json.dumps({'passwords': lines})) ") if echo "$JSON_BODY" | jq -e 'type == "array"' &>/dev/null 2>&1; then JSON_BODY=$(echo "$JSON_BODY" | jq '{passwords: .}') fi curl -s -X POST "$API_ENDPOINT/upload" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d "$JSON_BODY" | pretty else # Single password curl -s -X POST "$API_ENDPOINT/upload" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d "{\"passwords\": [$(echo "$ARG" | python3 -c "import sys, json; print(json.dumps(sys.stdin.read().strip()))")]}" | pretty fi elif [[ -f "$1" ]]; then ARG="$1" # Bulk lookup via POST: build JSON array from file lines JSON_HASHES=$(jq -Rn '[inputs | select(length > 0)]' < "$ARG" 2>/dev/null \ || python3 -c " import sys, json lines = [l.strip() for l in open('$ARG') if l.strip()] print(json.dumps({'hashes': lines})) ") # If jq constructed a plain array, wrap it; if python3 wrote full body, use it if echo "$JSON_HASHES" | jq -e 'type == "array"' &>/dev/null 2>&1; then JSON_BODY=$(echo "$JSON_HASHES" | jq '{hashes: .}') else JSON_BODY="$JSON_HASHES" fi curl -s -X POST "$API_ENDPOINT/search" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d "$JSON_BODY" | pretty else # Single hash via GET (URL-encode the input) ARG="$1" ENCODED=$(python3 -c "import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1], safe=''))" "$ARG" 2>/dev/null \ || python3 -c " import urllib.parse, sys print(urllib.parse.quote('$ARG', safe='')) ") curl -s "$API_ENDPOINT/search/$ENCODED" \ -H "Authorization: Bearer $TOKEN" | pretty fi