Codegate CTF 2019 - 7amebox-tiny_adventure
Table of Contents
0x00. Introduction
The basic structure is the same as 7amebox-name.
Global variables
int dog_count_0x1000;
int *dog_avatar_0x1003;
int *map_0x1303;
int sell_count_0x1306;
int hp_0x1309;
int power_0x130c;
int x_0x130f;
int y_0x1312;
Concept
)
)
)
)
)
>1
) =
# (\x23) = wall
) =
)
##############################################################
#@ #
# a f #
# #
# v z #
# i p #
##############################################################
After reading the stage.map file and loading it into memory, you move using w, a, s, d, and when you defeat a monster, you acquire the generated * to power up. There are also buy_dog and sell_dog menus for triggering vulnerabilities.
Goal
void
When you meet z in stage.map, the boss stage opens, and regardless of hp, if power is greater than or equal to 0x2bc, it prints the flag.
However, even if you defeat all the monsters on the map, you cannot make the power value greater than 0x2bc, so I decided to exploit by manipulating the map information.
0x01. Vulnerability
In the load_map_0x103() function that runs first after firmware loading, the global variables are initialized as follows:
int
Among these, dog_avatar_0x1003 is used as follows:
void
If memory allocation through syscall succeeds, dog_count_0x1000 increases and the allocated memory region address is written to that index.
However, since there’s no boundary check for dog_count_0x1000, when the value becomes 0x101, we can write a value to map_0x1303 which is after the dog_avatar_0x1003 array. Since map_0x1303 holds the memory address where the contents of stage.map are read and stored, manipulating this allows us to manipulate the map. Moreover, we can write 0x1000 bytes to the allocated memory for drawing avatar, allowing us to freely manipulate the map contents.
do )
>n
do )
>n
The problem is that only 0xfb allocations are made and it fails. When I checked the reason:
There are already allocated pages and the emulator only saves the 0x0 ~ 0xff000 region as the allocatable area.
=
=
= 0
...
...
= # 0x100000
...
Therefore, we need to increase the dog_count_0x1000 value, which acts as an index, from 0xfb to 0x101 using another vulnerability. Since the sell_count_0x1306 value is exactly that difference of 0x6, we can reasonably infer that we should use sell_dog_0x304().
void
However, looking at the code for sell_dog_0x304(), if the address to unmap is less than 0x100000, the syscall cannot be called. But I remembered that unmapping succeeded when I entered AAAA during dynamic analysis, so I checked the emulator code.
...
# munmap
= & 0b111111111000000000000
...
...
= & 0b1111
...
When sys_s6() is called in the emulator, it sets the page’s permission to 0b0000 through the Memory object’s set_perm() function. In this process, however, it doesn’t check if the page exists before setting the permission. And in Python’s dictionary, when you input a value to a non-existent key, it creates and stores a new key-value pair.
Therefore, even the non-existent region AAAA could be unmapped, and since this region is stored in the pages dictionary, allocation is also possible. Of course, if you try to write or read values after allocation, it exceeds the memory size and causes an error. However, since the purpose is to increase dog_count_0x1000, we just need to perform munmap -> map 6 times and write the map information to a writable area.
0x02. Exploit
The emulator’s memory allocation process is as follows:
return
return -1
return
return -1
When addr is not specified, it iterates through the pages dictionary and returns a page without the PERM_MAPPED permission.
In Python 2.7, dictionaries are traversed in random order, and even if you add values later, the order doesn’t come at the end but can be inserted in the middle.
# Python 2.7.18
>>> =
>>>
... print ,
...
>>> = 5
>>>
... print ,
...
Therefore, if we add a key-value to the Memory object’s pages dictionary, there are high chances that the key will be inserted in the middle. Indeed, if we follow the process of the emulator creating the pages dictionary and unmapping, we can get the following result:
>>> =
>>> # Memory.__init__()
... = 0
...
>>> = 0 # Memory.set_perm()
>>> # Memory.allocate()
...
... print %
...
0x100000 is 98-
>>> print %
0x78000 is 256-
Therefore, to allocate all regions except the last page at 0x78000, I structured the payload as follows:
Now dog_count_0x1000 has increased to 0xfa, and the method to increase 0x6 times more using sell_dog_0x304() is as follows:
Finally, when the remaining 0x78000 memory is allocated, dog_count_0x1000 becomes 0x101 and map_0x1303 gets overwritten with 0x78000.
Now the value input to draw the avatar becomes the map information as is, so the payload to fill the entire map with * meaning power up except for @(my location) and the z(boss) is as follows:
= b
+= b * 3598
+= b
Now all that’s left is to sufficiently increase power and fight the boss. The condition to beat the boss is that power must be greater than or equal to 0x2bc, and power up increases power by 0x5, so a minimum 121 times of power ups are needed. Since all positions on the current map are filled with *, we just need to move anywhere we haven’t been before 121 times.
0x03. Payload
=
=
=
return
return
return
return
=
=
= b
+= b * 3598
+= b
0x04. Decompile
char *str_0x6e2 = "====================================================\
| PWN ADVENTURE V8.6 |\
====================================================\
| __ |\
| _|^ |________ |\
| (____| |___ |\
| |________| |\
| | | | | |\
| |\
----------------------------------------------------";
char *str_0xc66 = "====================================================\
| YOU WERE DEAD! |\
====================================================\
| HP : 0 |\
| |\
| |\
| ... |\
| ___ |\
| |___| |\
----------------------------------------------------";
char *str_0x6a5 = " direction\
________________________________\
| W : north |\
| A : west D : east |\
| S : south |\
|________________________________|";
char *str_0x9c5 = "-------------------------------------------------\
* (\x2a) = power up\
# (\x23) = wall\
@ (\x40) = you\
a ~ y = monster\
z = boss monster (flag)\
-------------------------------------------------";
int dog_count_0x1000;
int *dog_avatar_0x1003;
int *map_0x1303;
int sell_count_0x1306;
int hp_0x1309;
int power_0x130c;
int x_0x130f;
int y_0x1312;
int
void
int
int
void
void
void
void
void
void