in the dark writeup
Challenge description
A lone mage, lost in the abyss of darkness awaits aid. Only a experienced wizard can guide him, perceiving what mortal eyes cannot see…
files :
or you can build it from source:
you need to compile it with:
gcc source.c -lssl -lcrypto -o chall
Solution
open chall in ida
in move_right() function (or any other) there is variable ‘MAP’
after inspecting the ‘MAP’ (right click -> array -> turn off ‘Use dup construnct’) we can see (or in gdb ‘x/100x (int*)&MAP’):
.rodata:0000000000002040 MAP db 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0
.rodata:0000000000002040 ; DATA XREF: gravity+3F↑o
.rodata:0000000000002040 ; move_right+2E↑o ...
.rodata:0000000000002052 db 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 2, 2, 2, 2
.rodata:0000000000002064 db 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
.rodata:0000000000002076 db 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0
.rodata:0000000000002088 db 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0
.rodata:000000000000209A db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0
.rodata:00000000000020AC db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1
.rodata:00000000000020BE db 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1
.rodata:00000000000020D0 db 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0
.rodata:00000000000020E2 db 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0
.rodata:00000000000020F4 db 1, 1, 0, 0, 0, 0, 2, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0
.rodata:0000000000002106 db 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0
.rodata:0000000000002118 db 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 1, 9
.rodata:000000000000212A db 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 2, 0, 0, 1, 1, 1
.rodata:000000000000213C db 1, 1, 1, 1, 1, 0, 0, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 0
.rodata:000000000000214E db 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
.rodata:0000000000002160 db 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0
also in
.rodata:0000000000002020 MAX_X db 1Eh
.rodata:0000000000002024 MAX_Y db 0Ah
MAX_X = 30 and MAX_Y = 10 we got map size 30x10
.data:0000000000004010 p_y dd 7 ; DATA XREF: gravity:loc_126F↑r
.data:0000000000004014 g_x dd 0Bh ; DATA XREF: goblin_move+8↑r
.data:0000000000004018 g_y dd 7 ; DATA XREF: goblin_move+18↑r
.data:000000000000401C g_dir dd 1 ; DATA XREF: goblin_move+50↑r
also from gdb:
pwndbg> x/d (int*)&p_x
0x555555558024 <p_x>: 0
pwndbg> x/d (int*)&p_y
0x555555558010 <p_y>: 7
pwndbg> x/d (int*)&g_x
0x555555558014 <g_x>: 11
pwndbg> x/d (int*)&g_y
0x555555558018 <g_y>: 7
pwndbg> x/d (int*)&g_dir
0x55555555801c <g_dir>: 1
so starting position of player is (0, 7) and goblin (11, 7) with direction 1 (right - direction is just added to x when moving)
- from this data we can construct the map with python script:
from PIL import Image
def draw_map(map, p_x, p_y, g_x, g_y):
for i in map:
print(i)
colors = {
0: (255, 255, 255), # white
1: (0, 0, 0), # black
2: (255, 0, 0), # red
9: (255,255,0) #yellow
}
img = Image.new('RGB', (MAX_X, MAX_Y), color='white')
pixels = img.load()
for y in range(MAX_Y):
for x in range(MAX_X):
if y < len(map) and x < len(map[y]):
if x == p_x and y == p_y:
pixels[x, y] = (0, 0, 255) # blue
elif x == g_x and y == g_y:
pixels[x, y] = (0, 255, 0) # green
else:
pixels[x, y] = colors.get(map[y][x], (255, 255, 255))
img = img.resize((MAX_X * 10, MAX_Y * 10), Image.NEAREST)
img.save('map.png')
img.show()
ida_dump = '''.rodata:0000000000002040 MAP db 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0
.rodata:0000000000002040 ; DATA XREF: gravity+3F↑o
.rodata:0000000000002040 ; move_right+2E↑o ...
.rodata:0000000000002052 db 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 2, 2, 2, 2
.rodata:0000000000002064 db 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
.rodata:0000000000002076 db 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0
.rodata:0000000000002088 db 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0
.rodata:000000000000209A db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0
.rodata:00000000000020AC db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1
.rodata:00000000000020BE db 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1
.rodata:00000000000020D0 db 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0
.rodata:00000000000020E2 db 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0
.rodata:00000000000020F4 db 1, 1, 0, 0, 0, 0, 2, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0
.rodata:0000000000002106 db 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0
.rodata:0000000000002118 db 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 1, 9
.rodata:000000000000212A db 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 2, 0, 0, 1, 1, 1
.rodata:000000000000213C db 1, 1, 1, 1, 1, 0, 0, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 0
.rodata:000000000000214E db 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
.rodata:0000000000002160 db 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0
'''
tmp = []
MAX_X = 30
MAX_Y = 10
p_x =0
p_y = 7
g_x = 11
g_y = 7
for line in ida_dump.split('\n'):
if 'db' in line:
for i in line.split('db ')[1].split(', '):
tmp.append(int(i))
map = []
for i in range(0, len(tmp), MAX_X):
map.append(tmp[i:i+MAX_X])
map.pop() # remove last null bytes
draw_map(map, p_x, p_y, g_x, g_y)
black - walls, red - lava, green - goblin, yellow - flag (from touching_lava(),goblin_move() and on_flag functions)
- after inspecting move_right(), move_left(), jump_left(), jump_right() (from decode_moves()) we can see what moves are allowed:
‘a’ - move_left() - one step left
’d’ - move_right() - one step right
‘wa’ - jump_left() - two vertical-up steps left
‘wd’ - jump_right() - two vertical-up steps right
also we have only 43 moves (chars) to use
other functions:
- gravity() - after each move player is moved down if possible
- touching_lava() - if player is on lava (red) loose
- on_flag() - if player is on flag (green) win and print flag from hashed input xored with secret key
- goblin_move() - goblin moves in his direction if encounters player he kills him, if encounters hollow space he changes direction
- we need to simulate possible moves and how goblin’s position changes:
from PIL import Image
def draw_map(map, p_x, p_y, g_x, g_y,filename='map.png'):
#for i in map:
# print(i)
colors = {
0: (255, 255, 255), # white
1: (0, 0, 0), # black
2: (255, 0, 0), # red
9: (255,255,0) #yellow
}
img = Image.new('RGB', (MAX_X, MAX_Y), color='white')
pixels = img.load()
for y in range(MAX_Y):
for x in range(MAX_X):
if y < len(map) and x < len(map[y]):
if x == p_x and y == p_y:
pixels[x, y] = (0, 0, 255) # blue
elif x == g_x and y == g_y:
pixels[x, y] = (0, 255, 0) # green
else:
pixels[x, y] = colors.get(map[y][x], (255, 255, 255))
img = img.resize((MAX_X * 10, MAX_Y * 10), Image.NEAREST)
img.save(filename)
#img.show()
def goblin_move(map,g_x,g_y,g_dir):
if map[g_y+1][g_x+g_dir] == 0:
g_dir *= -1
g_x += g_dir
else:
g_x += g_dir
return g_x, g_y, g_dir
def gravity(map, p_x, p_y):
while(p_y < MAX_Y and map[p_y+1][p_x] == 0):
p_y += 1
return p_x, p_y
def move_right(map, p_x, p_y):
return p_x + 1, p_y
def move_left(map, p_x, p_y):
return p_x - 1, p_y
def jump_right(map, p_x, p_y):
return p_x + 2, p_y - 2
def jump_left(map, p_x, p_y):
return p_x - 2, p_y - 2
def decode_moves(moves, map, p_x, p_y, g_x, g_y,g_dir):
i = 0
while i < len(moves):
if moves[i] == 'd':
p_x,p_y = move_right(map, p_x, p_y)
elif moves[i] == 'a':
p_x,p_y = move_left(map, p_x, p_y)
elif moves[i] == 'w':
if moves[i+1] == 'd':
p_x,p_y = jump_right(map, p_x, p_y)
elif moves[i+1] == 'a':
p_x,p_y = jump_left(map, p_x, p_y)
i += 1
i += 1
p_x, p_y = gravity(map, p_x, p_y)
g_x,g_y,g_dir = goblin_move(map,g_x,g_y,g_dir)
return p_x, p_y, g_x, g_y, g_dir
ida_dump = '''.rodata:0000000000002040 MAP db 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0
.rodata:0000000000002040 ; DATA XREF: gravity+3F↑o
.rodata:0000000000002040 ; move_right+2E↑o ...
.rodata:0000000000002052 db 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 2, 2, 2, 2
.rodata:0000000000002064 db 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
.rodata:0000000000002076 db 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0
.rodata:0000000000002088 db 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0
.rodata:000000000000209A db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0
.rodata:00000000000020AC db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1
.rodata:00000000000020BE db 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1
.rodata:00000000000020D0 db 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0
.rodata:00000000000020E2 db 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0
.rodata:00000000000020F4 db 1, 1, 0, 0, 0, 0, 2, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0
.rodata:0000000000002106 db 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0
.rodata:0000000000002118 db 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 1, 9
.rodata:000000000000212A db 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 2, 0, 0, 1, 1, 1
.rodata:000000000000213C db 1, 1, 1, 1, 1, 0, 0, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 0
.rodata:000000000000214E db 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
.rodata:0000000000002160 db 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0
'''
tmp = []
MAX_X = 30
MAX_Y = 10
p_x =0
p_y = 7
g_x = 11
g_y = 7
g_dir = 1
for line in ida_dump.split('\n'):
if 'db' in line:
for i in line.split('db ')[1].split(', '):
tmp.append(int(i))
map = []
for i in range(0, len(tmp), MAX_X):
map.append(tmp[i:i+MAX_X])
map.pop() # remove last null bytes
draw_map(map, p_x, p_y, g_x, g_y,filename='start.png')
moves1 = 'dwdwaawddddwddd'
p_x, p_y, g_x, g_y, g_dir = decode_moves(moves1, map, p_x, p_y, g_x, g_y,g_dir)
draw_map(map, p_x, p_y, g_x, g_y, filename='before_goblin.png')
moves2 = 'wd'
p_x, p_y, g_x, g_y, g_dir = decode_moves(moves2, map, p_x, p_y, g_x, g_y,g_dir)
draw_map(map, p_x, p_y, g_x, g_y, filename='after_goblin.png')
moves3 = 'dddddwdwawddddddwdwddaaaaa'
p_x, p_y, g_x, g_y, g_dir = decode_moves(moves3, map, p_x, p_y, g_x, g_y,g_dir)
draw_map(map, p_x, p_y, g_x, g_y, filename='final.png')
print(moves1+moves2+moves3)
output:
dwdwaawddddwdddwddddddwdwawddddddwdwddaaaaa
Stages:
starting position
before possible death from goblin - we need to jump over him
after avoiding gobiln
flag reached
██▓ ███▄ █ ▄▄▄█████▓ ██░ ██ ▓█████ ▓█████▄ ▄▄▄ ██▀███ ██ ▄█▀
▓██▒ ██ ▀█ █ ▓ ██▒ ▓▒▓██░ ██▒▓█ ▀ ▒██▀ ██▌▒████▄ ▓██ ▒ ██▒ ██▄█▒
▒██▒▓██ ▀█ ██▒ ▒ ▓██░ ▒░▒██▀▀██░▒███ ░██ █▌▒██ ▀█▄ ▓██ ░▄█ ▒▓███▄░
░██░▓██▒ ▐▌██▒ ░ ▓██▓ ░ ░▓█ ░██ ▒▓█ ▄ ░▓█▄ ▌░██▄▄▄▄██ ▒██▀▀█▄ ▓██ █▄
░██░▒██░ ▓██░ ▒██▒ ░ ░▓█▒░██▓░▒████▒ ░▒████▓ ▓█ ▓██▒░██▓ ▒██▒▒██▒ █▄
░▓ ░ ▒░ ▒ ▒ ▒ ░░ ▒ ░░▒░▒░░ ▒░ ░ ▒▒▓ ▒ ▒▒ ▓▒█░░ ▒▓ ░▒▓░▒ ▒▒ ▓▒
▒ ░░ ░░ ░ ▒░ ░ ▒ ░▒░ ░ ░ ░ ░ ░ ▒ ▒ ▒ ▒▒ ░ ░▒ ░ ▒░░ ░▒ ▒░
▒ ░ ░ ░ ░ ░ ░ ░░ ░ ░ ░ ░ ░ ░ ▒ ░░ ░ ░ ░░ ░
░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░
░
Fellow mage lost in the darkness, he needs your guidance to find the flag as fast as possible and survive...
dwdwaawddddwdddwddddddwdwawddddddwdwddaaaaa
Thank you for helping poor mage, in return you can see what he found.
zeroday{goblin_also_cant_see_in_the_dark}