Try   HackMD

HKCert 2022 CTF Writeup

Contents:

  1. Minecraft Geoguessr
  2. Fiddler Crab

Minecraft Geoguessr

It was 9 p.m. and I was banging my head against the wall trying to figure out how to solve the remaining 1-star challenges. As the second batch of problems were released, I immediately started to browse them and tried to find something more productive I could do.

While my teamates were all working on the easier challenges, I stumbled upon this challenge. A 5-star challenge that requires me to find the coordinates of a Minecraft image, worth 425 points. I mean it doesn't look that hard right? And its about Minecraft, my favourite game. Seems like the perfect task for me!

Official Description

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Do you know Rainbolt? If you send him a photo, he could immediately tell you where it was taken in.

Can you do the same in the Minecraft world?

Note: If you are desperate, you can watch Rainbolt's most insane geoguessr moments, or LiveOverflow's video series on Minecraft Hacking.

By the way, the map is generated with Minecraft 1.17.1. With that said, you may be unable to look for the better caves.

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

https://minecraft-geoguessr.hkcert22.pwnable.hk

Interpretation

We can see a Minecraft World with part of the flag built on it. We have to find the coordinates of this image and them use it to get the flag.
The website is a system that lets you take screenshots of the world. This is the only way to view the Minecraft world. It has an hCaptcha, which means we can't use a bot to take many screenshots at once.

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

The site also tells us that the possible range of values after pressing submit without inputting any values.

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Solution

"Why touch grass when you can study grass?" ~GeoRainbolt

Grass is a common feature Rainbolt uses to find where he is in GeoGuessr. What if I told you, grass can also tell you where you are in Minecraft?

Grass is one of the blocks in Minecraft whose texture is rotated randomly. However, the rotation of grass is only dependent on the x, y, z coordinates independent of the world seed.

From the decompiled Minecraft code, we can find the functions used to calculate the grass rotation. Here are the important bits (just for reference):

HashCode method in MathHelper class is used to generate a random seed from block coordinates:

public static long hashCode(int x, int y, int z) {
    long l = (long)(x * 3129871) ^ (long)z * 116129781L ^ (long)y;
    l = l * l * 42317861L + l * 11L;
    return l >> 16;
}    

Part of BlockRenderManager#RenderBlock method which passes the seed to render the block:

return this.blockModelRenderer.render(world, this.getModel(state), state, pos, matrices, vertexConsumer, cull, random, state.getRenderingSeed(pos), OverlayTexture.DEFAULT_UV);

Part of BlockModelRenderer#renderSmooth method which sets the seed of a the random object to the previously mentioned seed:

random.setSeed(seed);
List<BakedQuad> list = model.getQuads(state, direction, random);

WeightedBakedModel#getQuads which generates a long from the random object to get the face texture

public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction face, Random random) {
    return Weighting.getAt(this.models, Math.abs((int)random.nextLong()) % this.totalWeight).map(present -> ((BakedModel)present.getData()).getQuads(state, face, random)).orElse(Collections.emptyList());
}

Grass block model with 4 rotation variants with equal weight

{
  "variants": {
    "snowy=false": [
      {
        "model": "minecraft:block/grass_block"
      },
      {
        "model": "minecraft:block/grass_block",
        "y": 90
      },
      {
        "model": "minecraft:block/grass_block",
        "y": 180
      },
      {
        "model": "minecraft:block/grass_block",
        "y": 270
      }
    ],
    "snowy=true": {
      "model": "minecraft:block/grass_block_snow"
    }
  }
}

The seed generated from block coordinates is then used to generate random numbers to rotate the block.

From these algorithms, a script can be crafted to brute force the location of the image using some grass rotations we find on it. Luckily for us, such tool has already been created by fellow community member, 19MisterX98, who also made a seed cracker.

https://github.com/19MisterX98/TextureRotations

This Java program iterates through a range of coordinates to see if all calculated block rotations matches the inputted information. It also provides you with texture packs that can help with the recreation, though it only supports 1.16.5.

Recreation

We will be recreating it in Minecraft to make the process a whole lot less abstract, while also giving us the perfect excuse to play Minecraft in the middle of a CTF competition. We will use a texture pack provided by the tool that changes Lime Glazed Terracota, a rotatable block, to look like grass. It can then be freely rotated using the debug stick. It did take us a while to get it right without mistakes.

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

We ended up with 20 blocks. This may not sound like a lot, but there are already

420 possible combinations of rotations, so the probablility of it occuring more than once in a range of
40000×40000
blocks is very low.

We can then convert the rotation of the grass blocks to numbers with the second texture pack to be entered to the program.

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Before inputting the data, we first configure the range from the information we found above. We know that the y level is 66 because of the lake nearby, and water level is 62.

public static final int xMin = -20000, xMax = 20000;
public static final int zMin = -20000, zMax = 20000;
public static final int yMin = 66    , yMax = 66;

The infomation is to be added to an ArrayList called formation which is used by the program in the TextureFinder class. (0, 0, 0) is the center of the formation

formation.add(new RotationInfo(0, 0, 0, 1, false));
formation.add(new RotationInfo(0, 0, 1, 1, false));
formation.add(new RotationInfo(0, 0, 2, 3, false));
formation.add(new RotationInfo(-1, 0, 2, 0, false));
...

Since we do not know the orientation of the screenshot yet, we can create a method to rotate the rotation information clockwise so the program can also check different directions.

public static RotationInfo rotate(RotationInfo info) {
    return new RotationInfo(-info.z, info.y, info.x, (info.rotation + 1) % 4, info.isSide);
}

We then run the program a few times each time adding a rotation the whole list.

formation.replaceAll(Main::rotate);
...

We get a result when there are 2 rotations, i.e. facing north.

8:14:54 PM: Executing ':Main.main()'...

> Task :compileJava
> Task :processResources NO-SOURCE
> Task :classes

> Task :Main.main()
X: 16557 Y: 66 Z: 4171
3 seconds
...

Taking a screenshot near this coordinate, facing north (yaw = ) would show part of the flag.

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

By taking a few more screenshots, the whole flag can be found!

Flag

hkcert22{s33d_cr34kin9_1s_r3ver53_4nd_cryp70_com61ned}
Yup, not only that, but cracking your favourite video is a great way to train some of your advanced CTF concepts. Can't wait to see some similar problems in the future!

Fiddler Crab

Official Description

So you want to be a young GM Maybe train yourself against this AI-assisted chess server?

Or you can just cheat? Winning this can make you win Magnus Right?

P.S. The attachment is the same one from jumping-fish. You can just download it once.

Attachment: chess_4c333ec173e154a5101976cff17af195.zip

./chess-client chal.hkcert22.pwnable.hk:28046

Solution: https://hackmd.io/@blackb6a/hkcert-ctf-2022-i-en-3f8a9ef6

Intercepting the request

The official writeup for the solution mentions that we can use tcpproxy to intercept tcp requests. However I think Burp Suite is a better option as we can edit requests manually.

We can install the Nope Proxy plugin for Burp Suite to make it work with TCP requests.

First, we set up the proxy in the new tab that was added by the addon. Go to NoPE Proxy -> Server Config and input the server infomation and press the add button. It also has to be enabled.
The server's IP address can be found by typing
ping chall.hkcert22.pwnable.hk
in the Linux terminal.

Then we go to the TCP Intercept tab and turn on intercept.

Then run the chess client app with local IP address and the listener port.
./chess-client 127.0.0.1:1000

By pressing forward on Burp Suite we can send the current request see the next one.

Reverse Engineering

Lets try to make sense of the requests.

By moving a piece we can see a request like this.
9rnbqkbnr/pppppppp/8/8/8/7N/PPPPPPPP/RNBQKB1R b KQkq - 1 1
Searching it up, we find that this is actually FEN notaion. nbqkbnr/pppppppp/8/8/8/7N/PPPPPPPP/RNBQKB1R represents the chessboard. Small letter represents the opponent's pices while capital letters represent your own. Numbers represent empty spaces.

The First Byte

The first byte 9 is actually the total length of the FEN notation. From the example above, we can see that the length of the request excluding the first byte is 57, which is '9' when encoded in ASCII.

Knowing this, we can fiddle with the chesboard to our advantage.

Change all our pawns into queens and delete their pawns
1rnbqkbnr/8/8/8/8/8/QQQQQQQQ/RNBQKBNR b KQkq - 1 1

The next move we delete some of their other pieces.
4r1b1kb1r/4n3/8/8/8/7N/QQQQQQQQ/RNBQKB1R b KQkq - 3 2

It should then be quite easy to win, once we checkmate, the flag will be printed in the terminal.

Flag

hkcert22{m1tM-pr03y-c0u1d-h3lppp?e3en-st0ckf1sh15-c4n'7_w1n_u!!}