Strip Mine
Strip Mine
Overview
Hey heyyy 👋🏽
I originally wanted to make a dedicated blog post for a reversing challenge I solved on pwn.college, but… I got lazy. 😭
This challenge wasn’t graded with points; it was more of a tutorial challenge meant to teach some reversing concepts. Tbh, I’ve forgotten the exact details of the dojo and how the challenge was introduced, but welp. 😂
From what I remember, the challenge is basically a variant of the “Strip Mine” game, where the paths and grids are hardcoded into the binary.
So yeah, this is me finally writing about it before I forget even more. 💀
Source
This is the data structure in use (after reversing it):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
00000000 struct grid_t // sizeof=0x1B0
00000000 {
00000000 int score;
00000004 char grid[20][20];
00000194 int x;
00000198 int y;
0000019C // padding byte
0000019D // padding byte
0000019E // padding byte
0000019F // padding byte
000001A0 node_t *path;
000001A8 size_t steps;
000001B0 };
00000000 struct node_t // sizeof=0x10
00000000 {
00000000 uint8_t bits[8];
00000008 struct node_t *next;
00000010 };
And here’s the whole program decompilation.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
__int64 __fastcall main(int a1, char **a2, char **a3)
{
char *data; // rax
char *data_ptr; // rbx
ssize_t len; // rax
node_t *path; // rdi
node_t *next; // rbx
grid_t game; // [rsp+0h] [rbp-1C8h] BYREF
unsigned __int64 v10; // [rsp+1B8h] [rbp-10h]
v10 = __readfsqword(0x28u);
initialize_game(&game);
data = malloc(0x10000uLL);
if ( data )
{
data_ptr = data;
len = read(0, data, 0x10000uLL);
if ( len > 0 )
{
game.path = process_input(data_ptr, len, &game.steps);
if ( game.path )
{
free(data_ptr);
check_path(&game);
path = game.path;
if ( game.path )
{
do
{
next = path->next;
free(path);
path = next;
}
while ( next );
return 0LL;
}
else
{
return 0LL;
}
}
else
{
fwrite("error processing input\n", 1uLL, 0x17uLL, stderr);
free(data_ptr);
return 1LL;
}
}
else
{
free(data_ptr);
return 0LL;
}
}
else
{
perror("malloc");
return 1LL;
}
}
int *__fastcall initialize_game(grid_t *grid)
{
char *v1; // rax
char *v2; // rdx
int *p1; // rax
int *p2; // rax
v1 = grid->grid[0];
do // populates the grid to dots
{
v2 = v1 + 20;
do
*v1++ = '.';
while ( v1 != v2 );
v1 = v2;
}
while ( &grid->x != v2 ); // while v2 isn't equal to the end of 20x20 grid
p1 = &data1;
do
{
grid->grid[*p1][*(p1 - 1)] = '#'; // write format is data[i] = y, data[i-1] = x
p1 += 2;
}
while ( &data2 != p1 );
p2 = &data3;
do
{
grid->grid[*p2][*(p2 - 1)] = '$';
p2 += 2;
}
while ( &data4 != p2 );
grid->x = 0;
grid->y = 0;
grid->score = 0;
return p2;
}
node_t *__fastcall process_input(char *data, __int64 len, _QWORD *steps)
{
node_t *ptr; // rax
char *data_ptr; // r12
__int64 n; // r13
node_t *root; // r14
char byte; // dl
node_t *node; // rbp
__int64 i; // rbx
char bit; // dl
_BYTE nibble[8]; // [rsp+10h] [rbp-48h]
unsigned __int64 v13; // [rsp+18h] [rbp-40h]
v13 = __readfsqword(0x28u);
ptr = 0LL;
if ( len )
{
data_ptr = data;
n = 0LL;
root = 0LL;
LABEL_3:
byte = *data_ptr;
nibble[0] = *data_ptr >> 4; // upper nibble
nibble[1] = byte & 0xF; // lower nibble
node = ptr;
i = 0LL;
while ( 1 )
{
ptr = calloc(1uLL, 0x10uLL);
if ( !ptr )
break;
bit = nibble[i]; // do some bit manipulation of the each nibble
ptr->bits[0] = bit & 7;
ptr->bits[1] = (bit & 0xF) >> 3;
ptr->next = 0LL;
if ( root )
node->next = ptr;
else
root = ptr;
++n;
++i;
node = ptr;
if ( i == 2 ) // after two iterations check end of data_ptr
{
if ( &data[len] != ++data_ptr )
goto LABEL_3;
goto LABEL_12;
}
}
}
else
{
n = 0LL;
root = 0LL;
LABEL_12:
*steps = n;
return root;
}
return ptr;
}
__int64 __fastcall check_path(grid_t *game)
{
node_t *path; // rax
__int64 i; // rdx
__int64 steps; // rbp
int y; // ecx
int x; // edi
int current_score; // esi
__int64 pos; // rcx
unsigned int coord_x; // esi
unsigned int coord_y; // ecx
path = game->path;
if ( path )
{
for ( i = 1LL; ; ++i )
{
steps = i - 1;
if ( path->bits[1] )
{
y = game->y;
x = game->x;
if ( game->grid[y][x] == '$' )
{
current_score = game->score + 1;
game->score = current_score;
game->grid[y][x] = '.';
if ( current_score > 15 )
{
game_win();
return __printf_chk(1LL, "score: %d -- took %ld steps\n", game->score, steps);
}
}
path = path->next;
}
else
{
pos = path->bits[0];
coord_x = game->x + x_axis[pos];
coord_y = game->y + y_axis[pos];
if ( coord_y > 0x13 || coord_x > 0x13 || game->grid[coord_y][coord_x] == '#' )
{
puts("\nCOLLAPSE!!");
return __printf_chk(1LL, "score: %d -- took %ld steps\n", game->score, steps);
}
game->x = coord_x;
game->y = coord_y;
path = path->next;
}
if ( !path )
break;
}
steps = i;
}
else
{
steps = 0LL;
}
return __printf_chk(1LL, "score: %d -- took %ld steps\n", game->score, steps);
}
unsigned __int64 game_win()
{
int v0; // eax
int *v1; // rax
char *v2; // rax
int v3; // eax
int *v4; // rax
char *v5; // rax
int *v6; // rax
char *v7; // rax
_BYTE v9[136]; // [rsp+0h] [rbp-98h] BYREF
unsigned __int64 v10; // [rsp+88h] [rbp-10h]
v10 = __readfsqword(0x28u);
v0 = open("/flag", 0);
if ( v0 < 0 )
{
v1 = __errno_location();
v2 = strerror(*v1);
__printf_chk(1LL, "\n ERROR: Failed to open the flag -- %s!\n", v2);
if ( geteuid() )
{
puts(" Your effective user id is not 0!");
puts(" You must directly run the suid binary in order to have the correct permissions!");
}
exit(-1);
}
v3 = read(v0, v9, 0x80uLL);
if ( v3 <= 0 )
{
v4 = __errno_location();
v5 = strerror(*v4);
__printf_chk(1LL, "\n ERROR: Failed to read the flag -- %s!\n", v5);
exit(-1);
}
if ( write(1, v9, v3) <= 0 )
{
v6 = __errno_location();
v7 = strerror(*v6);
__printf_chk(1LL, "\n ERROR: Failed to write the flag -- %s!\n", v7);
exit(-1);
}
puts("\n");
return __readfsqword(0x28u) ^ v10;
}
Solve
For my solution, I ended up treating the maze like a graph and exploring it using Breadth-First Search (BFS) to find the path through the hardcoded grid.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from pwn import *
from collections import deque
exe = context.binary = ELF('stripmine', checksec=False)
context.log_level = 'info'
def start(argv=[], *a, **kw):
if args.GDB:
return gdb.debug([exe.path] + argv, gdbscript=gdbscript, *a, **kw)
else:
return process([exe.path] + argv, *a, **kw)
def init():
global io
io = start()
def generatePath(path):
result = bytearray()
for pos in path:
lower_bit = pos
upper_bit = 0
upper_nibble = (upper_bit << 3) | lower_bit
lower_bit = 0
upper_bit = 1
lower_nibble = (upper_bit << 3) | lower_bit
byte = (upper_nibble << 4) | lower_nibble
result.append(byte)
return bytes(result)
def solve():
points_x = bytes.fromhex("00 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF")
points_y = bytes.fromhex("FF FF FF FF FF FF FF FF 00 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 FF FF FF FF")
x_axis = [struct.unpack("<i", points_x[i:i+4])[0] for i in range(0, len(points_x), 4)]
y_axis = [struct.unpack("<i", points_y[i:i+4])[0] for i in range(0, len(points_y), 4)]
grid = [
[0,0,2,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,1,1,2,0,0,0,0,0,0,1,1,1,0,0,0,0],
[0,0,0,0,0,0,0,2,0,0,0,0,2,1,0,0,0,0,0,0],
[2,0,0,0,0,0,0,2,0,0,0,0,0,1,1,1,0,0,0,0],
[0,0,0,0,1,1,1,1,1,2,0,0,0,1,0,0,0,0,0,0],
[0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0],
[0,0,1,1,1,1,2,1,0,0,0,0,1,1,1,1,1,0,0,0],
[0,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0],
[0,0,1,0,0,0,0,2,0,1,1,0,0,0,0,0,0,0,2,0],
[0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0],
[0,0,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0],
[0,0,0,0,1,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0],
[0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,2],
[0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,2,0,1,1,1,1,1,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
]
rows, cols = len(grid), len(grid[0])
def bfs(start_x, start_y):
q = deque()
q.append((start_x, start_y, []))
visited = set()
visited.add((start_x, start_y))
while q:
x, y, path = q.popleft()
for pos in range(8):
nx, ny = x + x_axis[pos], y + y_axis[pos]
if 0 <= nx < cols and 0 <= ny < rows and (nx, ny) not in visited:
if grid[ny][nx] == 1:
continue
new_path = path + [pos]
if grid[ny][nx] == 2:
grid[ny][nx] = 1
return nx, ny, new_path
q.append((nx, ny, new_path))
visited.add((nx, ny))
return None
start_x, start_y = 0, 0
win_path = b""
while True:
result = bfs(start_x, start_y)
if not result:
break
start_x, start_y, path = result
print(f"Reached food at ({start_x}, {start_y}) with moves {path}")
win_path += generatePath(path)
io.send(win_path)
io.interactive()
def main():
init()
solve()
if __name__ == '__main__':
main()
Running it works!
ありがとうございます!😊

