Automatic solver for the First Contact challenge from Hack The Box.
The challenge provides a Two-Line Element (TLE) describing a satellite and the geographic coordinates of a ground station. The objective is to determine the time windows during the next 24 hours when the satellite is visible from the station.
The satellite is considered visible when its elevation is greater than 30° above the horizon.
This project automates the entire process:
HTB server
│
▼
Receive TLE + ground station coordinates
│
▼
Parse challenge
│
▼
Create satellite model
│
▼
Propagate satellite orbit
│
▼
Calculate visibility events
│
▼
Filter elevation > 30°
│
▼
Generate RISE / SET timestamps
│
▼
Send answer through TCP
│
▼
Receive next challenge
The HTB service sends challenges in the following format:
Challenge Sat 1
TLE:
DIGITWIN HTB
1 01337U ...
2 01337 ...
Station location:
(Lat,Long): 54.680197746339395,69.98725738496918
When will it be visible next?>
The answer must contain the timestamps of the visibility windows as space-separated UTC timestamps:
2026-08-27T05:34:30Z 2026-08-27T05:37:56Z ...
The server can send multiple satellite challenges sequentially, so solving them manually is inconvenient. The solver therefore keeps the TCP connection open and automatically processes every challenge.
- Python 3
sgp4skyfield
Dependencies:
sgp4
skyfield
Kali Linux may prevent installing Python packages globally because of PEP 668 (externally-managed-environment).
For this reason, the project uses a Python virtual environment.
sudo apt update
sudo apt install -y python3-venvCreate the virtual environment:
python3 -m venv .venvActivate it:
source .venv/bin/activateInstall the dependencies:
pip install -r requirements.txtAlternatively:
pip install sgp4 skyfieldVerify the installation:
python -c "from skyfield.api import EarthSatellite, Topos, load; print('Skyfield OK')"Run:
python3 solver.pyThe solver establishes the TCP connection with the HTB service and waits for the first challenge.
Example:
[+] Connecting to 154.57.164.77:31594
[+] Connected
[+] Automatic solver active
When the server sends a challenge, the solver automatically extracts:
- TLE line 1
- TLE line 2
- Ground station latitude
- Ground station longitude
It then calculates the visibility windows and sends the result automatically.
Example:
[+] Challenge detected
[+] Latitude : 54.680197746339395
[+] Longitude: 69.98725738496918
[+] Calculating visibility...
[+] Answer:
2026-08-27T05:34:30Z 2026-08-27T05:37:56Z 2026-08-27T07:12:57Z 2026-08-27T07:16:28Z ...
[+] Answer sent!
[+] Waiting for next challenge...
If the answer is correct:
Correct!
Challenge Sat 2
...
The process continues automatically until the server closes the connection.
The satellite is described using a Two-Line Element set.
A TLE contains orbital parameters that describe the satellite's orbit at a specific epoch.
For example:
1 01337U 00000A 26239.16548611+.00000000 00000-0 19815-5 0 281 1
2 01337 55.9855 39.9692 0066508 37.0687 40.3667 15.18600202207153
Important parameters include:
- Inclination
- Right ascension of the ascending node
- Eccentricity
- Argument of perigee
- Mean anomaly
- Mean motion
- Epoch
The TLE is not simply a set of static coordinates. It is an orbital model that must be propagated to obtain the satellite's position at future times.
The SGP4 (Simplified General Perturbations 4) model is used to propagate the satellite orbit from the TLE epoch to a requested time.
Conceptually:
TLE + time
│
▼
SGP4
│
▼
Satellite position
and velocity
The sgp4 library implements the SGP4 propagation algorithm.
The raw SGP4 output is expressed in the TEME reference frame. Converting that information into useful observer-relative coordinates requires additional astronomical/geodetic calculations. Skyfield provides this higher-level functionality.
The challenge provides:
Latitude
Longitude
For example:
Lat = 54.680197746339395
Lon = 69.98725738496918
These coordinates represent the observer on Earth's surface.
The solver creates a Skyfield Topos object:
station = Topos(
latitude_degrees=lat,
longitude_degrees=lon
)This allows the satellite's position to be evaluated relative to the ground station.
The important quantity for the challenge is the elevation angle.
The elevation describes how high the satellite appears above the local horizon.
Conceptually:
Satellite
*
/|
/ |
/ | elevation
/ |
---------------*----+
Station
Horizon
The challenge defines visibility as:
elevation > 30°
Therefore, the relevant threshold is:
ELEVATION = 30.0Instead of manually sampling the satellite position every second, the solver uses:
satellite.find_events(
station,
t0,
t1,
altitude_degrees=30.0
)Skyfield searches the requested time interval for events where the satellite crosses the specified elevation.
The returned event types are:
0 → rise above 30°
1 → culmination
2 → set below 30°
Therefore:
RISE
│
▼
Satellite crosses 30°
│
▼
CULMINATION
│
▼
Satellite reaches its maximum elevation
│
▼
SET
│
▼
Satellite falls below 30°
The challenge only requires the beginning and end of each visibility window, so culmination events are discarded.
For example:
RISE CULMINATION SET
│ │ │
▼ ▼ ▼
05:34:30 05:36:10 05:37:56
The answer therefore contains:
05:34:30 05:37:56
For every challenge, the solver creates a time interval:
now = datetime.now(timezone.utc)
end = now + timedelta(hours=24)This means the calculation is performed relative to the current UTC time.
The resulting interval is passed to find_events():
t0 = ts.from_datetime(now)
t1 = ts.from_datetime(end)Only events inside this 24-hour interval are considered.
The challenge explicitly allows the current visibility window to be skipped.
Therefore, if the satellite is already above 30° when the calculation begins, the first relevant event may be:
SET
The solver does not output that incomplete window.
Instead, it waits for the next:
RISE → SET
pair.
This prevents the answer from containing a visibility window that started before the requested calculation interval.
The solver does not require manually running nc.
It creates the TCP connection directly from Python:
sock = socket.create_connection(
(HOST, PORT),
timeout=10
)The server's response is continuously read:
data = sock.recv(4096)The received data is stored in a buffer.
When the solver detects:
When will it be visible next?>
it knows that the challenge is ready to be solved.
The challenge is parsed using regular expressions to extract the TLE and station coordinates.
After calculating the answer, it is immediately sent back:
sock.sendall(
(response + "\n").encode()
)The buffer is then cleared and the solver waits for the next satellite.
This creates the following loop:
Receive challenge
↓
Parse TLE
↓
Parse coordinates
↓
Calculate 24-hour visibility
↓
Generate timestamps
↓
Send answer
↓
Wait for next challenge
↓
Repeat
It would be possible to use the low-level sgp4 library directly, but that would require implementing additional calculations for the observer's position, Earth rotation, coordinate transformations, and topocentric altitude.
Skyfield is built around astronomical calculations and provides a higher-level interface for working with Earth satellites and observer locations.
The important call for this challenge is:
satellite.find_events(
station,
t0,
t1,
altitude_degrees=30.0
)This directly matches the problem definition.
The solver successfully automated the complete HTB First Contact challenge.
It processed multiple dynamically generated TLEs and ground station locations, calculated the corresponding visibility windows, submitted the answers automatically, and completed the challenge.
Correct!
Correct!
Correct!
HTB challenge completed.
This repository is intended for educational purposes and for use with authorized Hack The Box laboratory environments.
The solver is designed specifically for the HTB First Contact challenge.