Skip to main content

AIS3 2020 PreExam Write Up

My first experience with CTF, and probably the last one.

AIS3 2020 PreExam Write Up

📋 Problems #

CategoryProblemFinal ScoreSuccess SolvedWrite Up
🐧 Misc💤 Piquero100347✅
🐧 Misc🐥 Karuego100245✅
🐧 Misc🌱 Soy139172✅
🐧 Misc👑 Saburo359108✅
🐧 Misc👿 Shichirou45065
🐧 Misc🧸 Clara5002
♻️ Reverse🍍 TsaiBro100281
♻️ Reverse🎹 Fallen Beat144171✅
♻️ Reverse🧠 Stand up!Brain45562
♻️ Reverse🍹 Long Island Iced Tea49815
♻️ Reverse🌹 La vie en rose49912
♻️ Reverse🐉 Uroboros5009
💥 Pwn👻 BOF100189
💥 Pwn📃 Nonsense47447
💥 Pwn🔫 Portal gun49128
💥 Pwn🏫 Morty school49814
💥 Pwn🔮 Death crystal49910
💥 Pwn📦 Meeseeks box5008
🙊 Crypto🦕 Brontosaurus100380✅
🙊 Crypto🦖 T-Rex100381✅
🙊 Crypto🐙 Octopus372103
🙊 Crypto🐡 Blowfish48042
🙊 Crypto🐪 Camel49718
🙊 Crypto🐢 Turtle49814
🌐 Web🐿️ Squirrel100220
🌐 Web🦈 Shark100261✅
🌐 Web🐘 Elephant168165✅
🌐 Web🐍 Snake272137
🌐 Web🦉 Owl49227
🌐 Web🦏 Rhino49424

🐧 Misc #

💤 Piquero #

Piquero Braille

It seems quite obvious to me, this is indeed braille.

So the only thing you need to do is open up wikipedia,

lookup from the table and translate it into flag.

There are still some technique worth mentioned.

For instance, each capital letters, digits, punctuation marks have a special pre-block for indication,

and can be hard to lookup from regular table.

The solution, one way is to guess from other content, another way, for example:

if you’re trying to lookup â ¸ (dots on 4, 5, 6)

go to

Wikipedia will list all possible decode in different language/system.


🐥 Karuego #


This is a png image given by the problem.

First thing to do is decode the compressed zip file within the image with binwalk,

binwalk -e Karuego_0d9f4a9262326e0150272debfd4418aaa600ffe4.png

and then retrieve the password of the zip file with zsteg.

$ zsteg Karuego_0d9f4a9262326e0150272debfd4418aaa600ffe4.png
[?] 1201353 bytes of extra data after image end (IEND), offset = 0x1f6d30
extradata:0         .. file: Zip archive data, at least v1.0 to extract
    00000000: 50 4b 03 04 0a 00 00 00  00 00 40 8a bd 50 00 00  |PK........@..P..|
    00000010: 00 00 00 00 00 00 00 00  00 00 06 00 1c 00 66 69  ||
    00000020: 6c 65 73 2f 55 54 09 00  03 47 d3 d0 5e ed d3 d0  |les/UT...G..^...|
    00000030: 5e 75 78 0b 00 01 04 f5  01 00 00 04 14 00 00 00  |^ux.............|
    00000040: 50 4b 03 04 14 00 09 00  08 00 6f 21 07 49 ac 2c  |PK........o!.I.,|
    00000050: 71 1f 7c b9 01 00 d6 b9  01 00 35 00 1c 00 66 69  |q.||
    00000060: 6c 65 73 2f 33 61 36 36  66 61 35 38 38 37 62 63  |les/3a66fa5887bc|
    00000070: 62 37 34 30 34 33 38 66  31 66 62 34 39 66 37 38  |b740438f1fb49f78|
    00000080: 35 36 39 63 62 35 36 65  39 32 33 33 5f 68 71 2e  |569cb56e9233_hq.|
    00000090: 6a 70 67 55 54 09 00 03  71 44 a6 57 48 d3 d0 5e  |jpgUT...qD.WH..^|
    000000a0: 75 78 0b 00 01 04 f5 01  00 00 04 14 00 00 00 1d  |ux..............|
    000000b0: cd 40 aa 87 37 8a 57 93  85 a9 4c b3 cf fd 58 2d  |.@..7.W...L...X-|
    000000c0: 38 10 a2 2a dc 61 3e 8a  67 bf 4c c1 e1 cb d0 d0  |8..*.a>.g.L.....|
    000000d0: 32 65 97 d0 b5 ef f0 a5  2a 04 a6 00 af b2 63 e9  |2e......*.....c.|
    000000e0: 19 c2 6a 7b de 79 a3 a2  3f 0a b3 e8 74 67 35 ec  |..j{.y..?...tg5.|
    000000f0: 1b 8b 90 c9 76 30 ad ab  18 7a e9 9a ed f7 8e 7c  |....v0...z.....||
b1,rgb,lsb,xy       .. text: "The key is : lafire"
b1,bgr,lsb,xy       .. <wbStego size=865108, data=";M\xCD!,\xC5\xA0:\x807"..., even=false, enc="wbStego 2.x/3.x", controlbyte="\x80">
b3,r,lsb,xy         .. file: gfxboot compiled html help file
b3,bgr,msb,xy       .. file: Applesoft BASIC program data, first line number 2
b4,r,lsb,xy         .. file: PDP-11 UNIX/RT ldp
b4,g,lsb,xy         .. file: a.out VAX demand paged (first page unmapped) pure executable not stripped
b4,b,lsb,xy         .. file: Targa image data - Map 16 x 4096 x 16 +16 +4353 ""
b4,rgb,lsb,xy       .. file: Targa image data - Map (256-4112) 4096 x 65536 x 1 +4097 +257 - right ""
b4,bgr,lsb,xy       .. file: PDP-11 UNIX/RT ldp

In this case, the password is lafire,

decompress the zip file, there will be an image which contains the flag.

If you’re wondering how to find out all these, simply put, Googling.

To be more specific, Google keyword CTF Image.

Be careful of letter l and digit 1, it took me some time to distinguish those….


🌱 Soy #

Soy QR Code

QR Code fixing, nothing special.

It is, however, going to be helpful, if you have some prior knowledge to QR Code.

Take this empty QR Code as example:

Blank QR Code

Red section is for positioning, blue section is Format Info.

I fix the QR Code with QRazyBox, clicking the blue section will list all possible format.

Compare from the QR Code given, Error Correction Level: L,Mask Pattern: 0 is the only possible solution

The rest is just filling the blocks, once you have fill enough blocks, you’ll be able to decode the flag



👑 Saburo #


I didn’t really get the idea of this problem at first.

The only thing I discovered is you can get a slightly higher delay in milliseconds with AIS3{ as input.

I kind of know how to acquire the flag, but doesn’t seems legit to me, so I leave the puzzle for later.

Until the second night of the contest, I found that many contestent were complaining that the results were unstable.

I then realize, brute force is probably the only solution.

So I draft a Python script to iterate all ASCII Pritable letters, and do a while true loop until the presence of }.

Something look like this:

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("", 11001))

However it doesn’t work as I expected it to like it is in terminal, the delay stays low.

My second thought is to convert the script into a shell script, but I just can’t, I failed.

Out of the desperation, this is what I came up with:

Keyboard Maestro

If you don’t have a clue, this is called QuickMacro.

It’s basically just automatically executing a pre-record keyboard/mouse event.

I’m using Keyboard Maestro on Mac, but I believe any QuickMacro on any platform has the ability to achieve something similar.

It’ll work like this:

During this iteration, the corrrect letter is i, which is kind of hard to find the difference.

According to author, each letter tops up 5ms of noise.

So at the final stage of decoding, you have to run 10 times for each letter, calculate mean value to really find out the correct letter….

or really, just, use your


You are my Ene…. energy?

If you’re lack of imagination (like me)

just…. try it harder than.

The final letter has about 400ms of delay.


♻️ Reverse #

🎹 Fallen Beat #


Decompress the zip archive, fire up jar executable, you’ll soon realize this is a custom music game.

To acquire the flag, you’ll have to outbeat a full combo.

For those who really are trying:

Full Combo

Don’t even think about it.

First thing first, decompile jar executable.

There are dozen ways to achieve that,

my entry-level-approach will be….

online decompile, which doesn’t suck, to be honest.

The directory contains many java source code file,

global search for keyword flag,

you’ll find a method setValue within Visual/ contain somthing like

if (t == mc) {
    for (int i = 0; i < cache.size(); ++i) {
        final byte[] flag = this.flag;
        final int n = i % this.flag.length;
        flag[n] ^= (byte)(Object)cache.get(i);
    final String fff = new String(this.flag);
    this.text[0].setText(String.format("Flag: %s", fff));

You can see that flag is not stored with plain text, it’s generated by one of the class attributes flag

and xor with a cache that got pass in. Therefore, we’ll need to trace what is the cache that got passed in.

Do a second global search with SetValue, the result indicate that SetValue is only called in Control/

Take some time, you realize the cache is the class attribute within class,

so searching this.cache in this file can find out that this.cache is decalared inside the constructor

// ...
final FileReader fr = new FileReader(fumenPath);
final BufferedReader br = new BufferedReader(fr);
// ...
this.cache = new ArrayList<Integer>();
// ...
while (br.ready()) {
    final String s = br.readLine();
    if (s.charAt(0) != '*') {
        // ...
        // ...

Another problem here is that fumenPath is also a parameter that got pass in during the initialze of the class instance.

So, we’ll do a thrid global search with keyword GameControl.

Luckily, GameControl is only initalized once insideControl/

Finally, we found out that fumenPath is songs/gekkou/hell.txt.

Repacking eveything above into,

and move songs/gekkou/hell.txt to the same directory, then execute the java source code, you’ll be able to retrieve the flag.

Be careful that the br object that was used to read cache, reads an additional line before reading cache

this.bpm = Integer.parseInt(br.readLine());

so you’ll add this into, to prevent decoding error.


🙊 Crypto #

🦕 Brontosaurus #


It’s abvious that the final keyword KcufsJ is a reverse spelling of JsfucK

(well actually, you can findout that with Google, too)

It’s a custom JavaScript, you can found a decompiler with Google easily.

Fill in the code, which wouldn’t work, is beacuse you have to reverse it first.


🦖 T-Rex #


The keyword of this problem is nihilist,

which defined a person that who had a philosophical view that all knowledge and values are baseless,

which in fact, does not help solving this problem, at all.

But nihilist cipher did.

If you’re wondering how do I came up with an additonal keyword, cipher, the keyword is nihilist CTF or nihilist encoding.

Nihilist cipher are composed of two components, a polybius square and a encrypted text.

You can decode the text by matching the corresponding block in the square mattrix.

There are a lot of online tools, but I don’t find any being helpful,

so I made my own version with Python, take a look at if you’re interested.



🌐 Web #

🦈 Shark #


First look at the main page


Source code doesn’t reveal anything interesting, try clicking the link


The hint says that the flag is not located on the server that we’re visiting, but a server located in the same local network.

By obeserving the URL, you can assume that the path parameter can be used to view some file on the server.

I managed to know that unix system tends to store local network informations in /proc/net/fib_trie.

Therefore, our first attempt:

The access is forbidden, try somethign else:


Well, that worked.

You can see that the php source code blocks any access that make use of absolute path and any relative path starts with ...

This is where php:// came in.

php:// is a pseudo protocal, which is used to proces data stream

For instance, the exmaple given in php documentation:


loads the content of

So we changed the original URL from




All we need to do now is to analyze the content.

There are many tutorial on Google,

but an obvious way is by guessing that is for unicast, is for something realted to localhost.

The only possible answer are those begin with 172.

Combine with the hint given previously,

try this

doesn’t work.

Try again



🐘 Elephant #


Let’s see the landing page


not a clue, try a random name


from the keyword token, I assume, the previosu page is login interface,

output the flag according to the name given.

Normally, there will be a cookie after login, let’s see.


PHP cookies are normally base64 encoded, decode them first.


Next, according to a hint from this cheatsheet: w181496 / Web-CTF-Cheatsheet,

and the knowledge of any PHP string == boolean true.

By modifying the cookie into


then base64 encode, fill back into the browser.