gamepwnfreeeasy

NoMap3D

HackTheBox

Challenge provides an ELF 64-bit PIE executable (not stripped) — an SDL2 raycasting game in Wolfenstein 3D style, and an `assets.dmp` file (11.9 MB) containing map data, skybox, and textures. Unlike the related NoClip challenge which encoded the flag in individual character textures, NoMap3D encodes

$ ls tags/ techniques/
binary_asset_parsingmap_extractionascii_art_decodingwall_pattern_visualization

NoMap3D — HackTheBox

Description

"I'm lost in this green cube hell. I have to find a way out of it."

Challenge provides an ELF 64-bit PIE executable (not stripped) — an SDL2 raycasting game in Wolfenstein 3D style, and an assets.dmp file (11.9 MB) containing map data, skybox, and textures. Unlike the related NoClip challenge which encoded the flag in individual character textures, NoMap3D encodes the flag as ASCII art formed by the wall layout itself.

Flag format: HTB{...}

Analysis

Step 1: Initial Recon

file nomap3d # ELF 64-bit LSB pie executable, x86-64, not stripped, dynamically linked nm nomap3d | grep -E "player|raycast|colf|window|event|asset" # load_assets, player_init, raycast, event, input # colf.c, window.c, event.c, player.c, raycast.c

The binary uses the same SDL2 raycasting engine as the NoClip challenge. Key modules: collision detection (colf.c), player control (player.c), raycasting renderer (raycast.c), input handling (event.c), SDL2 window (window.c).

The name "NoMap3D" is a direct hint: "No Map" → the flag says "Who needs a map" in leetspeak. The map layout itself IS the flag.

Step 2: Parsing assets.dmp

The file uses the same chunk-based format as NoClip:

Chunk 1 (id=1): Player position

OffsetTypeValueDescription
0x00uint321Chunk ID
0x04double103.0Player X position
0x0Cdouble7.0Player Y position

Chunk 2 (id=2): Map data

OffsetTypeValueDescription
0x14uint322Chunk ID
0x18uint32259Map width
0x1Cuint3212Map height
0x20byte[]259×12×3Map data (9324 bytes)

Each map cell is 3 bytes (column-major order: map[x * height + y]):

  • Byte 0: Wall type — 0=empty, 1=standard wall, 2=wall_circuit texture
  • Byte 1: Side texture index (unused)
  • Byte 2: Floor texture — 2=floor, 3=arrow up

Chunk 3 (id=3): Skybox texture

  • Name: "skybox", 1024×906 RGBA

Chunk 4 (id=4): Textures — Only 3 generic textures:

IndexNameSizePurpose
0htb128×128HTB logo
1wall_circuit1024×985Wall texture
2floor_circuit1024×997Floor texture

Step 3: Key Insight — Map IS the Flag

Unlike NoClip (which had 18 textures including individual character textures), NoMap3D has only 3 generic textures. The flag is encoded differently: the wall layout itself forms ASCII art text.

The map is 259 columns × 12 rows — a wide horizontal banner. The outer border (y=0, y=11, x=0, x=258) is all wall_circuit walls. The inner area (y=1 to y=10) contains the flag text formed by standard walls (wall_type=1).

All floor tiles have arrow-up texture (floor_tex=3), meaning the player looks upward at the walls — a hint to visualize the map from above.

Step 4: Extracting the ASCII Art

Extracting the wall pattern for y=2 to y=8 (the 7-row letter area) reveals clear ASCII art characters:

 #####   ##### ############# ##########     ###  ######   ######     #### ####           ####                               ####       ####    ##########      #######                      #                    ###        #####        #               ###     
  ###     ###  #    ###    #  ###     ###  ##      ###      ###       #    ###         ##   ###                           ########   ########   ###     ###  ###     ###                   ###                    ##         ###       ###                 ##    
  ###     ###       ###       ###     ###  #        ###     ####     #     ###  ##    ##   ## ##             ###   ##           ##         ##   ###      ### #####                        #####                   ####     # ###     # ###   ## #####       #    
  ###########       ###       ##########  ##         ###   #  ###   #      #########  ##  ##  ##              #########      ####       ####    ###      ###   #######                   #   ###                  # ###   #  ###   #   ###    ###   ###     ##   
  ###     ###       ###       ###     ###  #          ### #    ### #       ###   ###  ## ##   ##              ###   ###        ###        ###   ###      ###       #####                #########                 #   ###    ###  ##########  ###   ###     #    
  ###     ###       ###       ###     ###  ##          ###      ###        ###   ###   ###   ##               ###   ###  #     ###  #     ###   ###     ###  ##      ###               #       ###                #    #     ###       ###    ###   ###    ##    
 #####   #####     #####     ##########     ###         #        #        ##### #####   ######   ########### ##### #####  ######     ######    ##########    #########   ########### ####     ###### ########### ###        #####     #####   ########   ###     

Step 5: Reading the Characters

Segmenting by all-space columns yields 20 character segments. Reading left to right:

SegCharacterNotes
0HTwo vertical bars, horizontal middle
1TFull top bar, vertical center
2BLeft bar with right bumps
3{Opening brace
4WTwo V-shapes
5hLowercase: left bar full height, right starts mid
60Oval with diagonal
7_Only bottom row
8nLowercase: only bottom 5 rows, arch shape
93Right-facing curves
103Same as seg 9
11DLeft bar, right curve
12SS-curves
13_Only bottom row
14ATriangle/pyramid
15_Only bottom row
16MLeft bar with right diagonals
174Vertical with crossbar
18pLowercase with descender to y=9,10
19}Closing brace

Solution

#!/usr/bin/env python3 """ HTB Challenge: NoMap3D (GamePwn) Solution: Extract flag from SDL2 raycasting game assets The game uses the same engine as NoClip, but the flag is encoded differently. Instead of character textures on special walls, the flag is ASCII art formed by the wall layout itself. The map is 259x12 - a wide horizontal banner. The "NoMap" name hints that the map IS the flag: "Who needs a map" in leetspeak. """ import struct def solve(): with open("assets.dmp", "rb") as f: data = f.read() offset = 0 # Chunk 1: Player position chunk_id = struct.unpack_from("<I", data, offset)[0] assert chunk_id == 1 offset += 4 player_x, player_y = struct.unpack_from("<dd", data, offset) offset += 16 print(f"Player start: ({player_x}, {player_y})") # Chunk 2: Map data chunk_id = struct.unpack_from("<I", data, offset)[0] assert chunk_id == 2 offset += 4 map_width, map_height = struct.unpack_from("<II", data, offset) offset += 8 print(f"Map size: {map_width}x{map_height}") # Map is stored column-major: map[x * height + y] with 3 bytes per cell # Byte 0: wall type (0=empty, 1=standard wall, 2=wall_circuit) # Byte 1: side texture index (unused) # Byte 2: floor texture (2=floor, 3=arrow up) map_data = data[offset:offset + map_width * map_height * 3] # Extract wall pattern as ASCII art (y=2 to y=8 is the main letter area) print("\n=== ASCII Art Wall Pattern ===\n") for y in range(2, 9): row = "" for x in range(1, map_width - 1): idx = (x * map_height + y) * 3 wall_type = map_data[idx] row += "#" if wall_type >= 1 else " " print(row) # The pattern clearly shows: HTB{Wh0_n33DS_A_M4p} flag = "HTB{Wh0_n33DS_A_M4p}" print(f"\n{'=' * 50}") print(f"FLAG: {flag}") print(f"{'=' * 50}") return flag if __name__ == "__main__": solve()

Related Challenges

  • NoClip (HackTheBox, GamePwn, medium) — Same SDL2 raycasting engine, but flag encoded in individual character textures on special walls along a spiral maze path
  • NoRadar (HackTheBox, GamePwn) — Same raycaster engine, flag encoded in entity waypoint trajectory in .rodata
  • CubeMadness1 (HackTheBox, GamePwn, easy) — Unity game with hidden flag in textures

$ cat /etc/motd

Liked this one?

Pro unlocks every writeup, every flag, and API access. $9/mo.

$ cat pricing.md

$ grep --similar

Similar writeups