Contents:
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!
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.
https://minecraft-geoguessr.hkcert22.pwnable.hk
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.
The site also tells us that the possible range of values after pressing submit without inputting any values.
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.
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.
We ended up with 20 blocks. This may not sound like a lot, but there are already
We can then convert the rotation of the grass blocks to numbers with the second texture pack to be entered to the program.
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.
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!
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
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.
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 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.
hkcert22{m1tM-pr03y-c0u1d-h3lppp?e3en-st0ckf1sh15-c4n'7_w1n_u!!}