Official Write-Up for THJCC Challenges I Created
TL;DR
The translation is from GPT so if there is any issue just dm me thx >w<
Super baby reverse
First, check the file type with file:
1 | alvin@suiseiiscute ~/Downloads> file THJCC_Super_Baby_Reverse |
It is an ELF binary, so next I loaded it into IDA.

Flag: THJCC{BaBY_r3v3rs3_f0r_beggin3r}
Fllllllag_ch3cker_again?
As usual, check the file type first:
1 | alvin@suiseiiscute ~/Downloads> file chal |
Again, it’s an ELF binary, so I loaded it into IDA.
1 | int __fastcall main(int argc, const char **argv, const char **envp) |
From the code, we can see it is a simple XOR.
1 | from struct import pack |
Flag: THJCC{A_Simpl3_R3v3r3_using_CPP_d0ing_X0R}
THJCC-anti-virus
As usual, check the file type first:
1 | alvin@suiseiiscute ~/Downloads> file anti-virus |
Okay, ELF. But no section header looks suspicious.
Let’s use binwalk:
1 | alvin@suiseiiscute ~/Downloads> binwalk anti-virus |
We can see this line:
1 | Copyright text: "Copyright (C) 1996-2025 the UPX Team. All Rights Reserved. $ " |
So the binary is packed with UPX.
Just unpack it:
1 | alvin@suiseiiscute ~/Downloads> upx -d anti-virus -o anti-virus-unpacked |
Then reverse it in IDA:
1 | int __fastcall main(int argc, const char **argv, const char **envp) |
In main, we see it calls start_challenge.
Let’s analyze start_challenge:
1 | unsigned __int64 start_challenge() |
In short, it validates your input. Your input must be XOR-encoded and also include a magic header. If validation passes => RCE.
What is the magic header? It calls check_command, so let’s follow that:
1 | _BOOL8 __fastcall check_command(__int64 a1) |
We find that the magic header must be THJCCAV.
And we also need to bypass this blacklist:
1 | if ( !strstr(haystack, "system") |
Then we can get RCE.
Now let’s craft an exploit:
1 |
|
This challenge was solved via reverse shell, but not everyone had reverse shell ready.
So here is a second exploit (thanks to Grissia):
1 | from pwn import * |
Flag: THJCC{An_3a3y_Ant1_Viru5_H0p3_y0u_enj0y_it}
幽々子の食べ物
I only created the first-stage VMP for this challenge, so I will only explain the first part.
After decompiling, we can notice a few key points:
1 | v3 = getenv("VMP_SKIP_AD"); |
This is anti-debugging, unless VMP_SKIP_AD = 1.
Next:
1 | v6 = (unsigned __int8 *)malloc(0x40FB0u); |
- Allocate a large buffer:
buf = malloc(0x40FB0) - Initialize it with
unk_402160(initial payload blob)
And we also see:
1 | v9 = (char *)memcpy(v8, &unk_443114, n); |
We can tell it uses XOR / an LCG stream cipher to decrypt 120 bytes of VM bytecode.
So I wrote a hook to unpack it:
1 |
|
How to bypass the anti-debugger at the beginning:
1 | export VMP_SKIP_AD=1 |
That’s enough.
Compile:
1 | gcc -shared -fPIC -O2 -Wall -Wextra -ldl unpacked.c -o unpacked.so |
Then run:
1 | export VMP_SKIP_AD=1 |
Then:
1 | file unpacked.bin |

Boom, solved.
THJCC-anti-virus-revange
From the challenge name, this is clearly the revenge challenge of THJCC-anti-virus. Yes, the English spelling in the challenge name is wrong (not intentional).
The core is still similar to the previous one: get RCE .w.
1 | if ( read(0, s, 7u) == 7 && read(0, &v3, 1u) == 1 && (v1 = read(0, v4, v3), v1 == v3) ) |
This function checks whether command execution is allowed.
Again, we find:
1 | if ( strncmp((const char *)a1, "THJCCAV", 7u) ) |
So the magic header is still THJCCAV.
And sub_2223:
1 | unsigned __int64 __fastcall sub_2223(__int64 a1, __int64 a2, int a3) |
Definitions:
C[i] = in[i]: ciphertext byte
prev = C[i-1], and prev=0 when i=0
K(i, prev) = sub_21C9(i, prev)
T[i] = v8[i]: intermediate value
Then:
And
*(_BYTE *)(j + a2) = (v8[j] >> 5) | (8 * v8[j]);
this line is doing bit rotation:
1 | char __fastcall sub_21C9(int a1, char a2) |
This function is:
byte_3010
1 | .rodata:0000000000003010 byte_3010 db 3Fh, 7Ah, 1Dh, 0E2h, 55h, 0B8h, 0Ch, 91h, 4Eh, 0D3h |
Also, there are blacklisted strings in .data:
1 | data:0000000000005080 off_5080 dq offset aSystem_0 ; DATA XREF: sub_2376+D4↑o |
So now we have both the decryption rules and the banned strings.
Let’s check the environment:
1 | RUN mv /flag.txt /flag-$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1).txt |
This appends random characters to the flag filename. Since flag is banned, we can use fl*.
Also cat is banned, so we can use tac.
In addition, I also banned / and spaces. We can use ${IFS} to replace spaces and ${HOME:0:1} to replace /.
Now let’s craft the exploit:
1 | from pwn import * |
Flag: THJCC{t4c_t4c_d1d_y0u_r3v3rs3_c4t???}
