Skip to content

Baz (WriteUp)

Roman Bykov edited this page Apr 24, 2018 · 1 revision

So, the service is a bar where you can mix some spirits into a drink. Also, you may want the bartender to memorize your recipe or prepare one of the existing recipes.

Implementation:

The service is written in C entirely (not counting some bits in assembly), and does not depend on anything, including libc. Because I felt like hey, there's too much useless stuff in libc, I'll write everything I need from scratch, and did that.
The HTTP server routes requests according to a hardcoded mapping of url prefix -> handler, where each handler is a program in bytecode for a special VM.
The VM has a VERY limited instruction set specialized for handling HTTP requests:

Now, a few words about data storage.

There is a database of key-value pairs, where the key is the name of a drink, and the value is the recipe. The recipe is encoded as a 32-digit number in base 33 (the number of available spirits). To make sure that the numbers corresponding to manually created drinks (which have few components) are not too small, the number is XORed with a large magic constant. For arithmetic operations the numbers are represented as 192-bit integers.
Apparently, such numbers can also be represented as valid flags, which are basically numbers in base 36 (0-9A-Z) of width 31. And this is actually the format used for storage.
The exact conversion mechanics reside here:

The code quality of the HTTP server is not brilliant, which is of course intended, and one could think of a request that would overflow some buffer and crash the service. But this would not lead to obtaining any flags. Instead, one should research the code of the HTTP handlers, specifically the 'memorize' handler. It contains an undocumented feature: if you write 'dunno' as the name of your drink, the bartender will choose the actual name himself. The logic is simple: the name is just the flag (recipe) stripped of all non-letters. This feature itself is completely harmless. But there is a little bug in the implementation. In case there is already a recipe with the same name, the handler must return an empty response with code 409 Conflict. However, due to a forgotten 'pop' instruction, the response is not empty, but contains the value of the conflicted record that was left in the stack from a previous method call.

So one could come up with a simple idea:

  1. Select an existing recipe that was probably placed by the checksystem. Let's say it's called 'NAME'
  2. Do the math to compute a recipe that encodes as something like NAME000000000000000000000000000=
  3. Save the recipe with the name 'dunno'
  4. The bartender tries to save the recipe as 'NAME', gets a conflict and returns the actual flag placed by the checksystem!

Why couldn't we just save some recipe as 'NAME' in the first place? Well, this would lead to the same conflict, but it's handled by another branch of code, which doesn't have the same bug.