Try   HackMD

HKCERT CTF 2023: Guide to handwavy challenges (I)

從零開始的新世界 / Re:Zero (Web)

  1. Read the challenge carefully. Check what kind of achievement is required.
  2. It appears the achievements are contradict. Are you suppose to get flag by completeing the game? If not, where should you check to modify the save? (keyword: local stroage)
  3. You have the folloinwg structure found. What kind of modification is required?
    Image Not Showing Possible Reasons
    • The image was uploaded to a note which you don't have access to
    • The note which the image was originally uploaded to has been deleted
    Learn More →
  4. Save and refresh the browser and the flag is located in console.

收到收到 / yes-I-know-I-know (Forensic)

  1. You will need wireshark to analyse pcapng file.
  2. You can learn how to use the tool from many great online source. However, for this challenge, you should be able to get most of the information you need from this button
    Image Not Showing Possible Reasons
    • The image was uploaded to a note which you don't have access to
    • The note which the image was originally uploaded to has been deleted
    Learn More →
  3. Explore around and see if anything interesting. Figure out what was being done by observing the traffics.
  4. Identify the methodology/tool used to perform that action and search for relevant information on the internet.
  5. Come up with a solution to extract information from relevant captured DNS packets.
  6. Profit

求旗簽名 (I) / Sign me a Flag (I) (Crypto)

Challenge Summary

In this challenge, we are given a remote oracle and is allowed to perform the below operations:

  1. sign [key_client] [message]: Signs an arbitrary message that does not contain the substring flag.
  2. verify [signature]: Verifies the signature for the message gib flag pls.

Let kC, kS and m be the client's key, the server's key and the message respectively. The signature of m is given by

Sign(kC,m):=HMAC-SHA256(kCkS,m).

Note that kS will not be given to the player. The goal is to recover the signature of the message gib flag pls while fixing kC to be 16 null bytes.

Partial Guide

The exclusive-or (XOR) operator

In this challenge, the XOR operation of two operands, a and b (i.e., ab) is given below:

def xor(a, b):
    return bytes(u^v for u, v in zip(a, b))

Specifically, when the two strings are of different lengths, the output string has a length of the shorter input string. For example,

000102161234567816=12355416000102031612345616=12355416

Now assume that k is a 16-byte, secret value, and assume that

k0016=4016,

then we know the first byte of k would be 4016.

Leaking byte-by-byte

🤔 Imaginations only. In this part, we will assume that we have infinite oracle calls to the server. We unfortunately only have 10 calls in reality.

We can recover the secret byte-by-byte with the above behaviour. This is how we leak the first byte with 256 calls:

  1. Compute s1^:=HMAC-SHA256(00,hello world) locally.
  2. Call Sign(00,hello world), Sign(01,hello world), Sign(02,hello world)
  3. Eventually, there will be a u1 such that Sign(u1,hello world)=s1^. In this case, the first byte of kS would be u1.

Subsequently, if we want the second byte of kS, we can

  1. Compute s2^:=HMAC-SHA256(0000,hello world) locally.
  2. Call Sign(u100,hello world), Sign(u101,hello world), Sign(u102,hello world),
  3. Eventually, there will be a u2 such that Sign(u1u2,hello world)=s2^. In this case, the second byte of kS would be u2.

We can repeat the above process until kS is recovered. We will be able to recover that with at most 16×256=4096 oracle calls.

You can read solve.py that implements the algorithm.

Optimizing the number of oracle calls

We can actually use one remote call to retrieve one byte. This effectively reduces the number of oracle calls from 4096 to 16. This is how we leak the first byte of kS:

  1. Call Sign(00,hello world) and denote it by s1^.
  2. Compute HMAC-SHA256(00,hello world), HMAC-SHA256(01,hello world), HMAC-SHA256(02,hello world),
  3. Eventually, there will be a u1 such that HMAC-SHA256(u1,hello world)=s1^. In this case, the first byte of kS would be u1.

We can repeat the process 16 times to fully reveal kS. Try to reduce the number oracle calls to below 10 with this idea!

ISA: 始料未及 / ISA Jump Scare (Pwn)

Challenge Description

In this challenge, we are asked to write a commentless program to read flag.txt. However, there is a catch: There is a checker that validates whether each line of the input program starts with a JMP.

Partial Guide

Let's first have a look at the below ISA code:

0x400000: JMP 0x40000d; JMP 0x400000
          ⬆️

0x40001b: NOP

The program control (PC) would be 0x400000 (i.e., beginning of the code) when we run the program. In this case, the instruction to be executed would be JMP 0x40000d.

Although 0x40000d is not a address of the beginning of an instruction, similar to a large number of interpreters available in real life, the interpreter simply parses the instruction starting from PC. In this case, 0x40000d points at the beginning of the JMP 0x400000 (which is intended to be a comment). The interpreter thinks that this would be the next instruction and decided to run JMP 0x400000 afterwards.

0x400000: JMP 0x40000d; JMP 0x400000
                        ⬆️

0x40001b: NOP

In this case, we can actually "write" two instructions in one line, and the comment is could be executed during runtime.


We will want to execute some other commands than JMP in the challenge. How do we achieve that? We can leverage the JMP instruction and jump to the address you want Maybe into the middle of an instruction?

Alas, the checker does not allow us to write comments. Fortunately the interpreter does not check whether the instructions are valid at the beginning. It would be fine as long as we are not invalid instructions. This would be handy in our case.

ISA: 世界與我有關? / ISA Intrusion (Reverse)

Challenge Description

In this challenge, we are asked to reverse a program written in Bauhinia ISA and get the flag. If you directly load the challenge and run, you will see the program terminated with exit code 65 (STEP COUNT EXCEEDED). You need to understand why this happens and see where the flag is.

Partial Guide

You can copy the code and load it into the "Debug Playground". There, you can add breakpoint (by clicking the line you want to stop), and step line by line after breakpoint.

To see what happened, we add a breakpoint at the first line, run the program, and click step multiple time to see where the program runs.

We notice the program never runs after line 5: it jumps back and forth at line 3-5.

Remove the breakpoint and click continue, we can see the final states of the registers: it shows the final value of PC is 0x40003b, which is the start of line 6. Notice this does not mean it finish running line 6, but instead means it finish running line 5 (as PC is set to the next line start after execution of the line).

Let's first look at the code block it loops and never exit:

ADD R1, 1;
LT R1, 100000;
JNZ -35;

This is a simple loop structure, adding 1 to R1 each loop, and if R1 < 100000, it loops. As the runner can only run 131072 instruction, this makes this exceed the step count.

Next, you have to find a method to optimize this: Such that the edited code does the same thing, but without running that many instructions. This act of editing the assembly code is called "patching", and it is a very useful technique for dynamic analysis in reverse engineering. We can use patching to run different things to make us understand the program more, bypass some restriction and so on.

After you patch the code to bypass the STEP COUNT EXCEEDED error, you should be able to make the program runs without errors. (i.e. with exit code 0). However, there is still no output in the terminal.

There are multiple options for you now:

  1. static reverse the asm to understand its full logic (so you can then find out how it hides)
  2. try to break at different point in the program, and inspect the memory (through the memory view). If the program hides the flag, it is likely that it is contained in the program memory at some point during the execution.