Skip to content

Latest commit

 

History

History
 
 

[Easy] Cubicle Riddle

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 

Description:

Navigate the haunting riddles that echo through the forest, for the Cubicle Riddle is no ordinary obstacle. The answers you seek lie within the whispers of the ancient trees and the unseen forces that govern this mystical forest. Will your faction decipher the enigma and claim the knowledge concealed within this challenge, or will the forest consume those who dare to unravel its secrets? The fate of your faction rests in you.

Objective:

  • The objective of this challenge is to implement an algorithm to find the min,max values of an array using python bytecode.

Flag

  • HTB{r1ddle_m3_th1s_r1ddle_m3_th4t}

Difficulty

  • Easy

Release

release/misc_cubicle_riddle.zip (f5bfddb7230c2b2cdbc96cac6348533533bd2a52172610929f7a21fb90fe46a1)

Challenge

We're greeted with a socket server waiting for our answer on a riddle. If we check the riddler.riddler.py code we can see that we construct a Python function using the FunctionType which is called _answer_func. As an argument FunctionType gets a Code object which basically represents byte-compiled executable Python code. After we construct _answer_func we check if it's results are the same with (min(num_list),max(num_list)). So we basically want to construct a function that returns a tuple with the min and max of an array.

We can see that this object has already most attributes already there. The most importants are the:

  • codeobject.co_consts
  • codeobject.co_varnames
  • codeobject.co_code

co_consts is a tuple containing the literals used by the bytecode in the function. In this case the tuple is: (None, self.max_int, self.min_int)

co_varnames is a tuple containing the names of the local variables in the function (starting with the parameter names). In this case the tuple is: ("num_list", "min", "max", "num")

The one that we need to complete is the co_code which is a bytestring representing the sequence of bytecode instructions in the function. We can see that we already have the start and the end of this function. Let's see what it does...

    (Pdb) dis.dis(_answer_func)
          0 LOAD_CONST               1 (1000)
          2 STORE_FAST               1 (min)
          4 LOAD_CONST               2 (-1000)
          6 STORE_FAST               2 (max)
          8 LOAD_FAST                1 (min)
         10 LOAD_FAST                2 (max)
         12 BUILD_TUPLE              2
         14 RETURN_VALUE

We can see that the function stores 1000 in the min var and -1000 in the max var. After that it returns a tuple of the form (min, max). We can also see that the parameter name num_list is loaded in the function. So we can recontruct the function as:

def _answer_func(num_list: list[int]):
    min: int = 1000
    max: int = -1000
    # missing code
    return (min,max)

In between our "answer" is loaded which is the remaining missing bytecode sequence. We can either construct from the start using Python's opcodes, or we can write the function, dissassemble it and get the part that we need.

>>> def _answer_func(num_list: int):
...     min: int = 1000
...     max: int = -1000
...     for num in num_list:
...             if num < min:
...                     min = num
...             if num > max:
...                     max = num
...     return (min, max)
... 
>>> _answer_func.__code__.co_code
b'\x97\x00d\x01}\x01d\x02}\x02|\x00D\x00]\x12}\x03|\x03|\x01k\x00\x00\x00\x00\x00r\x02|\x03}\x01|\x03|\x02k\x04\x00\x00\x00\x00r\x02|\x03}\x02\x8c\x13|\x01|\x02f\x02S\x00'

Last thing would be to return a list of the bytes of the sub-bytestring:

>>> [b for b in b'|\x00D\x00]\x12}\x03|\x03|\x01k\x00\x00\x00\x00\x00r\x02|\x03}\x01|\x03|\x02k\x04\x00\x00\x00\x00r\x02|\x03}\x02\x8c\x13']
[124, 0, 68, 0, 93, 18, 125, 3, 124, 3, 124, 1, 107, 0, 0, 0, 0, 0, 114, 2, 124, 3, 125, 1, 124, 3, 124, 2, 107, 4, 0, 0, 0, 0, 114, 2, 124, 3, 125, 2, 140, 19]

When we send the bytes (without the []) we'll get the flag:

htb nc 127.0.0.1 1337

___________________________________________________________

While journeying through a dense thick forest, you find    

yourself reaching a clearing. There an imposing obsidian   

cube, marked with a radiant green question mark,halts your 

progress,inviting curiosity and sparking a sense of wonder.

___________________________________________________________

> 1. Approach the cube...

> 2. Run away!

(Choose wisely) > 1

___________________________________________________________

As you approach the cube, its inert form suddenly comes to 

life. It's obsidian parts start spinning with an otherwordly

hum, and a distorted voice emanates from withing, posing a 

cryptic question that reverbates through the clearing,     

, shrouded in mystery and anticipation.                    

___________________________________________________________
> Riddler: 'In arrays deep, where numbers sprawl,
        I lurk unseen, both short and tall.
        Seek me out, in ranks I stand,
        The lowest low, the highest grand.
        
        What am i?'
        
(Answer wisely) > 124, 0, 68, 0, 93, 18, 125, 3, 124, 3, 124, 1, 107, 0, 0, 0, 0, 0, 114, 2, 124, 3, 125, 1, 124, 3, 124, 2, 107, 4, 0, 0, 0, 0, 114, 2, 124, 3, 125, 2, 140, 19

___________________________________________________________

Upon answering the cube's riddle, its parts spin in a      

dazzling display of lights. A resonant voice echoes through

the woods that says... HTB{r1ddle_m3_th1s_r1ddle_m3_th4t}
___________________________________________________________

Implementation

import telnetlib
import re

_payload = b'|\x00D\x00]\x12}\x03|\x03|\x01k\x00\x00\x00\x00\x00r\x02|\x03}\x01|\x03|\x02k\x04\x00\x00\x00\x00r\x02|\x03}\x02\x8c\x13'
_payload_string = ','.join(str(b) for b in _payload)
HOST = "127.0.0.1"
PORT = 1337

tn = telnetlib.Telnet(HOST, PORT)

print(" > Connected succesfully to server...")
tn.read_until(b"\n(Choose wisely) > ")
print(" > Read garbage...")
tn.write(b'1')
tn.read_until(b"\n(Answer wisely) > ")
print(" > Read some more garbage...")
tn.write(_payload_string.encode())
last_message = str(tn.read_all())


pattern = re.compile("HTB\{.*?\}")
match = re.search(pattern, last_message)
print(f" > Found the flag: {match.group()}")

Proof of Concept

htb python solver.py
 > Connected succesfully to server...
 > Read garbage...
 > Read some more garbage...
 > Found the flag: HTB{r1ddle_m3_th1s_r1ddle_m3_th4t}