Mother's Secret

Exploit flaws found in Mother's code to reveal its secrets. - by melmols

Recon

Even though the room description states that there is only a web server running on port 80, we hit up Nmap and scanned our target machine. We have two open ports: an HTTP server running on port 80 and an SSH server running on port 22.

Next, we run Gobuster to enumerate some directories; on the first try, we get an error; to overcome this, we exclude the length of 42, but did not find anything of interest.

To check out what's happening, we visit a custom route. We get the status :"You just hit the wrong route.". Ok, let's head to the index of the page.

We are presented in a menu-like window with the status above about crew membership. We can navigate through the tiles using our UP and DOWN keys. The first tile states something about an Alien Loader. A YAML loader to parse and load YAML data. This loader holds the truth to unveil the hidden paths - Ok.

The second tile gives us some big hints for later use. That there are secrets beyond the intended boundaries and that those might be reachable using relative file paths.

The role tile gives us just the role. Currently we are a Crew Member.

And the flag tile might contain a flag value, but it is redacted.

The Source

Looking at the source of the page, we see that a ./index.min.js JavaScript file is loaded.

By trying to investigate it, we see it is minified (as the name states) and also obfuscated using non-readable function names and renaming those in variables.

To be able to get some more information and be able to read it better, we use a JavaScript beautifier.

Here, we can see that there are several functions. Some authentication takes place in yamlSocket. We get the path /yaml at line 34 for the YAML socket. The data is being modified depending on the values of authNostormo and authYaml. Those variables are preset to false using the statement ![] and set by the sockets. Line 96 holds a bunch of HTML code. Beginning at line 102, key navigation is implemented.

The Routes

Next, we look at the routes provided by the challenge.

In routes.txt, there are three functions defined on how to respond to HTTP POST requests on the root path "/" (in the yaml.js section, beginning at line 17), the /nostromo path, and the /nostromo/mother path in the Nostromo.js section (at lines 60 and 82).

In the yaml.js section, there is an isYaml function defined that takes a filename as a parameter and checks whether the given filename has a .yaml extension. If so, it returns true.

To reach a successful response on the path "/" root, a filename in the request body has to be provided in the variable file_path, which is a file ending with .yaml, and be present in the directory of the machine at ./public. If we are able to provide this file, it gets loaded and sent. By providing the wrong file, we should get the response "Not a YAML file path.".

To reach a successful response on the /nostromo path, there has to be a filename provided that is present in the ./public folder of the machine, but this time it does not have to be a .yaml file. Upon successful response, the variable isNostromoAuthenticate is set to true, and the data from the provided file is loaded and sent. By providing the wrong file, we should get the response "Science Officer Eyes Only".

To reach a successful response on the /nostromo/mother path, there has to be a filename provided that is present in ./mother of the machine, and the variables isNostromoAuthenticate and isYamlAuthenticate set to true. It gives the hint to visit the nostromo and yaml routes first to set those variables to true. The variable isYamlAuthenticate is imported from yaml.js. By providing the wrong file, we should get the response "Science Officer Eyes Only".

The input for of file_path does not get sanitized in any of the request handlers. This may result in a path traversal vulnerability and allow us to read arbitrary files on the system.

routes.txt
API ROUTES

------------------------------------------

yaml.js
------------------------------------------

import express from "express";
import yaml from "js-yaml";
import fs from "fs";
import { attachWebSocket } from "../websocket.js";

const Router = express.Router();

const isYaml = (filename) => filename.split(".").pop() === "yaml";

Router.post("/", (req, res) => {
  let file_path = req.body.file_path;
  const filePath = `./public/${file_path}`;

  if (!isYaml(filePath)) {
    res.status(500).json({
      status: "error",
      message: "Not a YAML file path.",
    });
    return;
  }

  fs.readFile(filePath, "utf8", (err, data) => {
    if (err) {
      res.status(500).json({
        status: "error",
        message: "Failed to read the file.",
      });
      return;
    }

    res.status(200).send(yaml.load(data));

    attachWebSocket().of("/yaml").emit("yaml", "YAML data has been processed.");
  });
});

export default Router;
------------------------------------------
´

Nostromo.js
------------------------------------------

import express from "express";
import fs from "fs";
// import { attachWebSocket } from "../../mothers_secret_challenge/websocket.js";
import { attachWebSocket } from "../websocket.js";
import { isYamlAuthenticate } from "./yaml.js";
let isNostromoAuthenticate = false;

const Router = express.Router();

Router.post("/nostromo", (req, res) => {
  let file_path = req.body.file_path;
  const filePath = `./public/${file_path}`;

  fs.readFile(filePath, "utf8", (err, data) => {
    if (err) {
      res.status(500).json({
        status: "error",
        message: "Science Officer Eyes Only",
      });
      return;
    }

    isNostromoAuthenticate = true
    res.status(200).send(data);

    attachWebSocket()
      .of("/nostromo")
      .emit("nostromo", "Nostromo data has been processed.");
  });
});

Router.post("/nostromo/mother", (req, res) => {
 
  let file_path = req.body.file_path;
  const filePath = `./mother/${file_path}`;

  if(!isNostromoAuthenticate || !isYamlAuthenticate){
    res.status(500).json({
      status: "Authentication failed",
      message: "Kindly visit nostromo & yaml route first.",
    });
    return 
  }

  fs.readFile(filePath, "utf8", (err, data) => {
    if (err) {
      res.status(500).json({
        status: "error",
        message: "Science Officer Eyes Only",
      });
      return;
    }

    res.status(200).send(data);

    // attachWebSocket()
    //   .of("/nostromo")
    //   .emit("nostromo", "Nostromo data has been processed.");
  );
});

export default Router;



------------------------------------------

The route /yaml was found by analyzing index.min.js. It is the root path. We craft a simple POST request to check if that path is correct, and we get the response "Not a YAML file path.". We are on the right track.

While we are at it, we try to check for the nostromo path; unfortunately, with our first guess of just hitting on /nostromo we get the status "You just hit the wrong route.".

We remember a hint or a question in the challenge if we can guess the secret at /api/nostromo/mother/secret.txt.

We use the route /api/nostromo and get the status message "Science Officer Eyes Only". Next, we try to check for /api/nostromo/mother and get the hint to visit both routes mentioned before. So, we are on the right track, we have the correct path, but we are missing out on the correct or existing filenames we have to provide. That we have to pass filenames, we know from the analysis of the routes file.

Emergency Command Override

We head to the first question and have to provide the number of the emergency command override. In the room description, there is an operation manual in which the number is directly mentioned. The manual states we have to use it when accessing the Alien Loaders.

Below are some sequences and operations to get you started. Use the following to unlock information and navigate Mother:

  • Emergency command override is 100375. Use it when accessing Alien Loaders.

  • Download the task files to learn about Mother's routes.

  • Hitting the routes in the right order makes Mother confused, it might think you are a Science Officer!

It is 100375.

Special Order Number

So, we have the order number and know that we have to use it on the Alien Loaders aka YAML Loader. This file might be located at ./public. We hit up cURL and made a POST request on /yaml.

To provide a file, we use the -d tag. The file does not have to be in our directory. First, we test with an example file. If it is not present, we get the same error.

┌──(0xb0b㉿kali)-[~/Documents/tryhackme/Mother's Secret]
└─$ curl -X POST -H "Content-Type: application/json" -d '{"file_path": "test.yaml"}' http://10.10.71.12/yaml

Upon using 100375 as the filename, adding .yaml to it, (see Recon: The Routes ) we are able to retrieve some information. The orders are stored in a txt file, and a rerouting to /api/nostromo takes place, a hint to guide us to /api/nostromo.

We get the same results when providing the route /api/nostromo. Both are accessing the file from ./public. The name of the txt file contains the special order number.

Hidden Flag In The Nostromo Route

To retrieve the hidden flag in the nostromo route, we request the txt file mentioned in 100375.yaml.

Name Of The Science Officer

Now, that we have visited the /yaml and /nostromo paths with a successful response, the authentication values are set. Next, we just call modifyData() in the console to load the data as the authenticated user, and we are able to retrieve the name of the science officer in the role tile. It is not necessary to set the parameters; just calling modifyData() reveals the information.

Classified Flag

As we are already authenticated as the officer, we are also able to retrieve the flag from the classified flag box.

Alternative Way To Science Officer's Name and Classified Flag

Another way to retrieve the science officer's name and the classified flag is to analyze the source code further. We just have to look at the contents of the variable _0x491022 at line 96. By replacing the encoded spaces \x20 in hex with spaces, it is easier to read. For this we are using cyberchef. In there, we are able to retrieve the science officer name in clear text and the flag encoded in base64.

Mother's Secret

Now, we just have to get the Mother's Secret. The room description challenges us to guess the mother's secret and provides us an api path we already know from our discoveries before:

We just try to access this secret mentioned in the room description, maybe it leads us to more information. And we get a hit, the secret.txt contains the location of the mother's secret. It is in a location not in ./public or ./mother.

Recalling the Pathways tile from the page and the missing sanitization in the request handlers of the provided routes file, we should be able to get beyond this boundary using relative paths.

We make use of the relative path to access the file mentioned in secret.txt and are able to retrieve the final flag.

Trying To Go Further

Out of interest, we checked for users in /etc/passwd, and we have only ubuntu and root as users able to log in identified.

We were able to access the /etc/shadow file, but we see that the accounts are disabled for logging in and no passwords are present.

We were also able to identify two public keys, stored in ./.ssh/authorized_keys of each user's home directory.

But trying to access the corresponding private keys on, for example, /root/.ssh/id_rsa or /home/ubuntu/.ssh/id_rsa unfortunately fails.

Last updated