tl;dr
- Dynamically resolved hashed API
- Tls_call_back based anti-debug check
- AntiDebugFlag check implemented using ProcessInformationClass
- AES_CBC decryption of image to find flag
tl;dr
tl;dr
tl;dr
Challenge author: Freakston, silverf3lix
Senpai plis find me a way.
This is a fairly simple maze challenge implemented in rust.
At the start of the main function of the challenge miz we can see there is a some sort of a initialization going on at a function named miz::bacharu::Kikai::new::h3a113b790cc2bb5c
We can see from here than this function takes care of initializing the maze. We can extract the maze bytes either directly from here or during runtime, which ever is preferred.
Moving forward we can see that the function is also getting our input, and sending it over to another function miz::bacharu::Kikai::run::h14398f1fc265e61e
The function miz::bacharu::Kikai::run
takes care of the maze logic, the up, left, down and right.
We can see that the case “h” takes care of going left, which is by subtracting 1 from the y coordinate
Similarly, case “j” is to go up, it does this by subtracting 1 from the x coordinate.
Case “k” takes care of going down the maze, it adds 1 to the x coordinate
This case takes care of going right in the maze, it does so by adding 1 to the y coordinate
From this function we can also get the bounds of the maze which is 24 x 25, where there are 25 rows and 24 columns.
And the properties of the maze are,
We can also see that this is the function miz::bacharu::Kikai::Furagu_o_toru::hd3e3c2fb2ccf3552
is the win function, and this is called when we traverse till we reach the number 2 in the maze.
Constructing the maze should be easy since we have its bounds,
1 | miz = [[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] |
The start position can be retrieved while debugging and it is (0, 13). The end position is
(24, 19)
To solve this maze we can make use of the python library mazelib
Here is the script,
1 | from mazelib import Maze |
The final moves to be sent as input comes out to be,
llkkhhhhkkkkhhhhjjhhhhhhkkllkkkkkkhhkkllkklljjlllllljjhhjjllllllkklljjllkklljjllkkkkhhhhkkkkllkkkkhh
Flag: inctf{mizes_are_fun_or_get}
tl;dr
Challenge Points: 245
Challenge Solves: 20
Challenge author: EvilMuffinHa
Solved by: AmunRha, Freakston, barlabhi
This is a simple VM which has around 25-27 opcodes with instructions simple enough to be emulated. This is a stack based VM.
The VM implements several constraints on the input bytes which can be solved using z3 SMT solver.
The VM implemented a puzzle called kenken
I chose python to write the disassembler in with several helper functions, at first I tried extracting the constrains one by one, which eventually worked, but then I was able to write a automatic extractor for the disassembly.
There were two files, one the binary and the data file in which the list of instructions contain.
This when fed to the z3 solution script will get us the required input.
Most of the operations take their operands from the stack, so there wasn’t much complexity in terms of implementation.
p.s. This will be a short write up
1 | def disasm(op, c): |
Commenting the lines specified can get us the extracted constrains.
I wrote a small parser on my disassembly which will get the proper constraints.
1 | equations = {"add_2":[], "add_3":[], "equate_3": [], "equate_4":[], "mod_equate_2":[], "sub_2":[], "equal_2":[]} |
There were in total 8 different constraints applied on the input bytes, which was added to z3.
1 | def set_idx(set_to): |
Running the script gives us the disassembly, and the extracted constraints
and pasting the extracted constraints to z3, gives us the input to be given,
pB738150rHt60714NP501s92420G3xUY013;Wo{=69h42Ob736B1y{@?1047uw`6
Sending this over the given nc connection, gives us the flag,
Flag: flag{kenken_is_just_z3_064c4}
Links to required files,
2k Disassembler Script - 2k_disassembler.py
Helper Script - helper.py
z3 solver script - z3_solver.py
tl;dr
A brief write-up of the intended solution of P1ayground challenge from InCTF Internationals 2020
tl;dr
Intended solution of Wannavmbe challenge from InCTF Internationals 2019
tl;dr
tl;dr
Challenge Points: 769
Challenge Solves: 7
Solved by: R3x
Initial analysis shows us that there are minor changes between this binary and the signal_vm binary - in the way the VM works. Please refer to the writeup of signal_vm for learning about the VM structure.
In the first stage of the challenge - we had access to all of the VM registers since they were all in the parent itself. Now in signal_vm_de1ta they are all in the memory space of the child - This makes it hard for us to track what is happening in the VM since we aren’t able to directly view its memory or registers.
The VM uses the same four signals (SIGTRAP, SIGILL, SIGSEGV and SIGFPE) to serve as instruction classes. However there are a few significant differences from the first stage.
The VM(parent) uses PTRACE_PEEKDATA and PTRACE_POKEDATA to read and write data into the child memory which contains the memory and the registers.
We tweaked the script for the old challenge to work for this one. Since we don’t have the register and memory states this time as that happens in the child, we decided to go ahead and write our own code to parse the instructions. So we were able to predict the contents of the VM registers accurately which helped us in figuring out what the child did.
1 | import gdb |
This was probably the most complicated VM algorithm I have seen in CTFs. I have written the python version of the code below - you can take a look at it.
1 |
|
Looking a bit deeper into the algorithm we see that it is actually taking the numbers in a very specific order.
x | z = ((x * (x + 1)) >> 1) | range of y + z |
---|---|---|
0 | 0 | 0 |
1 | 1 | 1..2 |
2 | 3 | 3..5 |
3 | 6 | 6..9 |
4 | 10 | 10..14 |
… | … | … |
100 | 5050 | 5050..5150 |
From this order we figured out that this was basically dividing the array in form of a triangle and then trying to find the path which has the maximum sum.
Now we know what the VM is trying to do and it is taking a long time since the VM is trying to bruteforce the path. Now all we need to do is to find a more efficient way to solve this.
Since it is copying the path that has the maximum sum. I printed out the entire array in the form of a triangle and then I searched for the flag format manually - that is de1ctf{
and then I followed it until I reached the end.
You can probably trace - ~triangle~is
from the above screen shot. That was like a wrapper around the flag.
flag was
de1ctf{no~n33d~70-c4lcul473~3v3ry~p47h}
After talking to the admin at the end of the CTF I learned that this was a DP problem and the solution was pretty simple.
You can take a look at the problem statement and the solution techniques here.
tl;dr
Challenge Points: 500
Challenge Solves: 21
Solved by: R3x, silverf3lix, Ayushi
Challenge takes an input - and running strace we see that it forks a child and then does some ptrace calls.
1 | ~> strace ./signal_vm |
Taking a look into the binary for a better understanding we come across the main function.
1 | signed __int64 sub_40172D() |
This leads us to understand that the code is basically forking and trying to establish some communication between the child and parent using ptrace.
Run the binary on gdb with set follow-fork-mode child
to observe the behaviour of the child. We get SIGILL
.
Let take a close look at the disassembly of the child.
1 | push rbp |
This is strange - looks like the child is made to trigger the signal. This leads us to the conclusion that the parent is responsible for handling the signal and continuing the execution of the child somehow.
Now lets take a look at what is happening in the parent. On reversing the function handler
we come to the following conclusions.
First thing to understand the role ptrace actually plays. Strace gives us -
1 | ptrace(PTRACE_GETREGS, 1763, NULL, 0x7ffc4cb9c0e0) = 0 |
Having not seen anything other than PTRACE_TRACEME - we start digging into the man page.
The
ptrace()
system call provides a means by which one process (the “tracer”) may observe and control the execution of another process (the “tracee”), and examine and change the tracee’s memory and registers.
PTRACE_PEEKTEXT/POKETEXT - Read/Write a word at the address addr in the tracee’s memory.
PTRACE_GETREGS/SETREGS - Read/Write into the registers of the tracee.
The parent has handlers for the following signals and each of them define a certain class of instructions:
move
classlogical
classcompare
classjump
classNow the following script skims through the signals triggered and parses them to give a set of readable instructions which decreased our work.
1 | import gdb |
From the instructions given out by the above script we were able to deduce that it is basically Hill cipher.
The key Matrix is a 7x7 one generated from the string below
1 | .data:00000000006D5100 aAlmostHeavenWe db 'Almost heaven west virginia, blue ridge mountains',0 |
The ciphertext matrix can be found from the instructions generated by the above script.Then we used sagemath to do the math for us.
1 | from sage.all import * |
Running the above script gave us the flag =>
de1ctf{7h3n_f4r3_u_w3ll_5w337_cr4g13_HILL_wh3r3_0f3n_71m35_1_v3_r0v3d}