Hubert Hackin''
  • All posts
  • About
  • Our CTF

NSEC21 Rare Metal Sequencer, pt. 2 - Mon, May 24, 2021 - Lucas Bajolet Barberousse

| Rev | Nsec21

Rare Metal Sequencer 2

As mentioned in part 1, solving the first step makes a second challenge available.

We’ll open it in ZSNES, as with the first rom:

seq2_data

Alright, the output is a bit garbled, but the UI looks similar enough.

No need to guess here, we’ll go straight into the code so we can see how it’s made.

Note: We’ll go through the main differences between the two (the binaries are quite similar in their handling).

First, let’s head to the input management function, @0x81e5!

Input function

This time, we’ve got a bit more data to keep track of:

input_routine

We can see a similar handling for both the bytes pertaining to the inputs of controller one, i.e.:

  • 0x4219 -> 0x1414
  • 0x4218 -> 0x1418

So that part hasn’t changed, the mapped addresses are the same, however, two more repeats are visible:

  • 0x421b -> 0x1424
  • 0x421a -> 0x1428

These bytes belong to the inputs of controller 2.

Remainder for the mapped buttons/addresses:

Controller 1: c1-map Controller 2: c2-map

So we will need to map both if we plan to successfully input the right sequence to get our flag.

Moving on, we can take a look at the first input check:

check-p1

OK, we have a bit more to consider this time.

Let’s go step by step, shall we:

  • LDA 0x1428 -> load the AXLR button line for controller 2 into A
  • CLC -> clear carry flag, since we do ADC afterwards, that’ll ensure it’s not garbage
  • ADC 0x1414 -> add the values of the BYsSUDLR button line for controller 1
  • ADC 0x1418 -> add the values of the AXLR button line for controller 1
  • AND 0xff -> keep only the last byte
  • BEQ 0x00826c -> so after all ADC are done, A must have value 0 in order to proceed

After that, we’ve got the final button check:

button-check

So in this case, we must input button Y on controller 2, which leads to the famed:

input1ok

Alright, so by the same logic, we should be able to get all the combinations:

  • Input 2:
    • None of the buttons in lines 0x1414, 0x1418 and 0x1428
    • 0x80 in line 0x1414, so B on controller 1
  • Input 3:
    • None of the buttons in lines 0x1418 or 0x1428
    • (@0x1418 + @0x1428) ^ 0xA0 == 0, so AL on either controller
  • Input 4: 0x1414 » 1 => 0x20, so 0x1414 must be 0x40, which is effectively button Y on controller 1.

Anyway, that’s the theory.

Note: for proof that it’s working

ok_seq_2

What we actually did

But, we misunderstood some part of the pre-conditions, and thought we had to effectively press 9 buttons at the same time on some occasion.

So that was not an option.

In its stead, we used wits (which should also perfectly work for part 1).

See, the flag is actually pre-stored in the binary, however, we do need to get the right parts.

This is actually done in the same routine as the remainder of the operations, in case you press the start button on controller 1.

start_to_flag

We can follow the flow to the end here:

part-1-flag

We’ll focus on this part, the logic is very similar for the remainder of the binary.

So here, we load the base address of the FLAG string (at 0x88a9), and we add to it the value at address 0x1400.

However, what we have at 0x1400 is actually the value of the buttons from 0x1424, when input 1 is successfully entered.

So, by that logic, we can go back to each input, and infer the value of each address we depend upon for decoding our flag:

  • 0x1400 -> 0x40
  • 0x1404 -> 0xF1 (0x80 ^ 0x71)
  • 0x1408 -> 0xA0
  • 0x140c -> 0x20

We can then read the amount specified for the loop counter, and append it to the FLAG- prefix.

  • I1: 0x88A9 + 0x40 -> 0x88E9, 6 bytes to read: QYR88W
  • I2: 0x88A9 + 0xF1 -> 0x899A, 5 bytes to read: NDGBW
  • I3: 0x88A9 + 0xA0 -> 0x8949, 5 bytes to read: 7HP3G
  • I4: 0x88A9 + 0x20 -> 0x89C9, 10 bytes to read: XRGW49GE5W

We then got the expected output: FLAG-QYR88WNDGBW7HP3GXRGW49GE5W.

Back to Home


Hackez la Rue! | © Hubert Hackin'' | 2024-05-31 | theme hugo.386