Skip to content

Instantly share code, notes, and snippets.

@Dave1840438
Last active January 17, 2019 01:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Dave1840438/e6b1c0fc84883513993b18db075fdda0 to your computer and use it in GitHub Desktop.
Save Dave1840438/e6b1c0fc84883513993b18db075fdda0 to your computer and use it in GitHub Desktop.
Warp pipes writeup - Montréhack Jan 2019

Warp pipes

There are several approaches to solve this challenge, I'll describe two of them here.

Challenge preview

Static Solution

In order to get the flag, you don't even have to play the game. You can open it in your favorite disassembler (I used IDA).

Looking at the functions, there is an interesting one : World::hasPlayerReachedEnd. By looking at the references, we can see it's being called from GameState::Update.

Fiddling arround in the update function, we see a check where it compares to 0x0B, which is the number of levels in the game.

GameState::Update function

When the variable is equal to 11, we see that the game pushes a state with the ID 7, let's find out which one it is.

In the enums tab of IDA, we can find the states IDs and the state corresponding to the ID 7, which is the GameOverState.

Enums

By looking around in the GameOverState class, we stumble upon strange looking data in the class' constructor.

Strange looking data

This data is then passed into a std::transform and set as the text of an sf::Text object, suspicious!

Pseudocode

Digging deeper in the transform call, we can see that the lambda applied to the data is a simple xor.

Lambda

The only step left is to extract the data and xor it, which then yields the flag: DCI{Mario_would_be_proud}

There are surely multiple starting points that will lead to the flag, that was the one I followed.

Dynamic solution

The dynamic solution is much simpler, here I'll use gdb.

First off we'll disable the case sensitivity to make sure we don't miss anything. The gdb command for that is: "set case-sensitive off".

Then we can lookup for variables containing the word "level".

Variables

Bingo! We have a static variable named GLOBAL_VARIABLES::currentLevel, we can surely use that to warp around the levels.

Starting a new game, we can see that the variable is at 0, let's set it to 10 (if 0 is the first level, 10 would be the last one). Set it by pausing the process with ctrl+C, modify the variable with : "set GLOBAL_VARIABLES::currentLevel = 10", and then resume the process with the "continue" command.

Nothing happened, let's try to jump into the portal.

Tada!

Tada

Alternative solutions

You could also patch the function in which the damage is applied, buff your stats or even freeze your health by constantly updating it from an external script (like Cheat Engine does). Get creative!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment