miscfreemedium

Pivot Chain Challenge

hackthebox

Task: Find minimum detection risk path through a network graph. Solution: Dijkstra algorithm with priority queue for weighted shortest path.

$ ls tags/ techniques/
api_interactiongraph_traversaldijkstra_algorithmpriority_queue

Pivot Chain Challenge — HackTheBox

Description

After bypassing CygnusCorp's perimeter defenses and cracking the PIN to gain internal access, you've uncovered a crucial piece of the network: the Core Administration Server. Your goal is to pivot laterally through the internal network to reach the server, but the path is not clear. The network is heavily monitored, and each host you move through carries a detection risk. Can you navigate the network stealthily, identify the safest paths, and reach the Core Administration Server without being detected?

Target: 94.237.120.233:40330

Analysis

Reconnaissance

  • Connected to the service — discovered an HTTP server with a web code editor
  • Page title "Pivot Chain" with an interface for solving challenges
  • Found API endpoint /run for submitting code in Python, C, C++ or Rust

Challenge Format

  • Input: N (hosts), M (paths), start host, target host
  • Then M lines of edges: source destination risk
  • Output: minimum total detection risk from start to target
  • Constraints: 5 <= N <= 150000, 6 <= M <= 10^6
  • Example: 5 hosts, 6 paths, optimal path host_1→host_2→host_3→host_4→host_5 with total risk 26

Algorithm Selection

This is a classic shortest path problem in a weighted directed graph:

  • Dijkstra's algorithm — optimal for finding minimum cost path
  • heapq from Python — efficient priority queue implementation
  • Complexity: O((V + E) log V)

Solution

import sys import heapq from collections import defaultdict def solve(): input_data = sys.stdin.read().strip().split('\n') # Parse first line: N M start target first_line = input_data[0].split() N = int(first_line[0]) M = int(first_line[1]) start = first_line[2] target = first_line[3] # Build graph graph = defaultdict(list) for i in range(1, M + 1): parts = input_data[i].split() src = parts[0] dst = parts[1] risk = int(parts[2]) graph[src].append((dst, risk)) # Dijkstra algorithm dist = {start: 0} pq = [(0, start)] # (risk, node) while pq: curr_risk, node = heapq.heappop(pq) if node == target: print(curr_risk) return if curr_risk > dist.get(node, float('inf')): continue for neighbor, edge_risk in graph[node]: new_risk = curr_risk + edge_risk if new_risk < dist.get(neighbor, float('inf')): dist[neighbor] = new_risk heapq.heappush(pq, (new_risk, neighbor)) print(-1) # No path found solve()

Submitting the Solution

import requests code = '''...''' # code above response = requests.post( 'http://94.237.120.233:40330/run', json={'code': code, 'language': 'python'} ) print(response.json()) # {"challengeCompleted":true,"flag":"HTB{st34lthy_p1v0t1ng_compl3t3}"}

Key Indicators

Use this technique when:

  • The task involves finding minimum/optimal path in a graph
  • Edges have weights (cost, risk, time)
  • Need to find a path from point A to point B
  • Weights are non-negative (for Dijkstra)
  • Large constraints require an efficient O((V+E) log V) algorithm

Alternative Approaches

AlgorithmWhen to use
DijkstraNon-negative weights, single source
Bellman-FordNegative weights, detecting negative cycles
Floyd-WarshallAll pairs of vertices, small graph
BFSUnweighted graph (all weights = 1)
A*Heuristic available for estimating distance to target

Notes

  • Using defaultdict(list) simplifies graph construction
  • Check curr_risk > dist.get(node, float('inf')) prunes stale entries in the queue
  • Early exit upon reaching the target saves time
  • For very large graphs, C++ can be used for speed

$ cat /etc/motd

Liked this one?

Pro unlocks every writeup, every flag, and API access. $9/mo.

$ cat pricing.md

$ grep --similar

Similar writeups