We just finish the finals yesterday, it was difficult, and we managed to get 2nd place yeah!
Here is some writeups of the challenges
Challenges
Corrupt (Forensics)
Challenges file:
We are given a PNG image file, view it with Photo viewer looks blank..
Check it with file
command it stated it only have 1x1 pixel:
file flag.png
flag.png: PNG image data, 1 x 1, 8-bit grayscale, non-interlaced
But it has 696 bytes, so I guess the size has been modify
Basically we need to change the PNG to the correct size to view the flag
After some research, found out we can modify the size of PNG using Hexeditor, based on this article
But how to we know the original PNG size??
Running pngcheck
also shown no errors
pngcheck -v flag.png
File: flag.png (696 bytes)
chunk IHDR at offset 0x0000c, length 13
1 x 1 image, 8-bit grayscale, non-interlaced
chunk pHYs at offset 0x00025, length 9: 5039x5039 pixels/meter (128 dpi)
chunk tEXt at offset 0x0003a, length 25, keyword: Software
chunk IDAT at offset 0x0005f, length 581
zlib: deflated, 32K window, default compression
chunk IEND at offset 0x002b0, length 0
No errors detected in flag.png (5 chunks, -69500.0% compression).
Find the original size
After some researching, I came across this article
In the pixel data part, we can see it calculate the expected IDAT size based on width and height
Therefore, another way around we can also calculate width and height based on IDAT size!
To find IDAT data size, we need to decompress the zlib compression
It got many ways to do it, I use linux command to do it
First, I use binwalk
to extract the zlib data
binwalk -e flag.png
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 PNG image, 1 x 1, 8-bit grayscale, non-interlaced
99 0x63 Zlib compressed data, default compression
Then go to _flag.png.extracted
, running zlib-flate
will decompress the zlib data, and wc
will count the data size
zlib-flate -uncompress < 63.zlib | wc
0 0 20100
Can see the data size is 20100
, put it in the equation:
height x (1 + width x bytesPerPixel) = 20100
Next we need to find bytesPerPixel
, if is RGBA image each pixel is 4 bytes long
This image is 8-bit grayscale, doing a quick google search can find it is 1 byte
Therefore, the equation become:
height x (1 + width) = 20100
We can guess the height and width is 200 or 100
Lets try to edit the size using hexeditor
Hex of 200 is c8
and 100 is 64
:
View it in Photo Viewer, and we saw the flag!!
Althought we didn’t solve this is finals, but still fun to solve it after
Parent Sharp
Challenge file:
We get a windows executable, running file
command can see it is a .NET assembly (Means it written using microsoft .net framework)
file Parent_Sharp.exe
Parent_Sharp.exe: PE32+ executable (console) x86-64 Mono/.Net assembly, for MS Windows
We can view the source code and even debug it using dnSpy in windows
Using dnSpy to open Parent_Sharp.exe
, we are able to see all of the source code inside:
Press Start to run it, it just print some words and exit:
Baby Shark dududududu
Find the mini shark dododododo
Find the naugthy shark dadadadada
fsdududududucyberx is not the flag dududu
Notice after running it, got a new file MessageFromAuthor.txt
add to same directory.. interesting
Inside the file is just Baby Shark dodododo Baby Sharp dudududu
Debug and analyze
Same as C program, it also have a Main function
Inside the main function we can see it got some byte array like pwx
, dada
dada2
dada3
dada4
Then it pass into a function in KJASD
:
string @string = Encoding.UTF8.GetString(KJASD.cHalala(pwx, dada));
string string2 = Encoding.UTF8.GetString(KJASD.cHalala(pwx, dada2));
string string3 = Encoding.UTF8.GetString(KJASD.cHalala(pwx, dada3));
string string4 = Encoding.UTF8.GetString(KJASD.cHalala(pwx, dada4));
We can set a breakpoint to see what is the string returned
Just behind the line number then it set a breakpoint at this line:
Click start, when enter the breakpoint the Locals
tab will appear some variables
After that, click Step Over
or F10 to move forward
Then you will see the string kernel32.dll
return in Locals:
Continue to step over, we can see the 4 strings return is:
"kernel32.dll"
"CreateFileA"
@".\MessageFromAuthor.txt"
"Baby Shark dodododo Baby Sharp dudududu"
Below got string5 which returns WriteFile
Notice the MessageFromAuthor.txt
we saw before, we can guess the code below is just importing CreateFileA
WriteFile
from kernel32.dll
and create the file with the name and write the file with the content
After that, you will see a large chunk of code with base64:
byte[] bytes = Encoding.ASCII.GetBytes("sqVv//z////7////AAD//0f/////////v///////////////////////////////////////////////f/////HgRfH/S/Yy3kf+szLeq5eWjN+PjZCYjZ6S35yekZGQi9+dmt+NipHflpHfu7Cs35KQm5rR8vL12/////////+vuv//m3n9/1yWUWX//////////w//3d/0/c///73////7///////////////f//////9//v/////f/////f//+//////////5//////////9f/////f////////z/n3r//7////////+////////////v////////3//////////////v//////////////////////////9///93/P//////////////////////////////////Y5///8f////////////////////////////////////////////////////////////////////////////////f//+3///////////////Ri5qHi////66+////3////73////9///////////////////f//+f0Y2MjZz///93/P///3/////7////u///////////////////v///......");
Type type = Assembly.Load(new bsd(Encoding.UTF8.GetString(bytes).ToCharArray()).gdgdgd()).GetType("Baby_Sharp.shark_call");
Console.WriteLine(Assembly.Load(new bsd(Encoding.UTF8.GetString(bytes).ToCharArray()).gdgdgd()));
object obj = Activator.CreateInstance(type);
Console.WriteLine(((string)type.GetMethod("dudu").Invoke(obj, null)).Substring(0, 87));
type.GetMethod("rktrkt");
Console.WriteLine((string)type.GetMethod("Sharp").Invoke(obj, new object[]
{
array
}));
Basically the base64 is assembly machine code (also known as shellcode)
You can see it decode it and pass it into Assembly.Load
:
Type type = Assembly.Load(new bsd(Encoding.UTF8.GetString(bytes).ToCharArray()).gdgdgd()).GetType("Baby_Sharp.shark_call");
Then it calls methods inside the Baby_Sharp.shark_call
class, 3 methods dudu
,rktrkt
and Sharp
So how do we decompile or disassembly the bytes code??
Source of Baby_Sharp
Thanks to dnSpy, we no need to disassembly manually, we just need to keep clicking Step Into
until it reach the Baby_Sharp
methods
Then you will see the source code
First, we need to understand how to use the Step Into
Step Over
and Step Out
when debugging
- Step Into is go into the next function
- Step Over is skip the next function
- Step Out is skip the current function
When it execute this statement:
Console.WriteLine(((string)type.GetMethod("dudu").Invoke(obj, null)).Substring(0, 87));
First it will call GetMethod
then Invoke
, Substring
then WriteLine
We want to get into Invoke
function, so we skip the first function
Steps to get in
- Set breakpoint before it call
dudu
- Click start then click step over and step into, you will see it get into the
Invoke
function
- Keep clicking step into, eventually it will reach the
dudu
function
- After that, the source code of
Baby_Sharp
is added in the left panel:
Solving
Lets see the Sharp
function:
public string Sharp(byte[] kay)
{
if (File.Exists(".\\MessageFromAuthor.txt"))
{
return "fsdududududucyberx is not the flag dududu";
}
shark_call.rktrkt(shark_call.dada);
byte[] bytes = shark_call.cHalala(kay, shark_call.dada);
return Encoding.UTF8.GetString(bytes);
}
As you can see, it if the file MessageFromAuthor
exists
If exist it returns the output we seen when we test run
Lets try what will happen when we delete the file in when running
Set a breakpoint before calling Sharp
:
Click start then delete the MessageFromAuthor.txt
then click continue
Baby Shark dududududu
Find the mini shark dododododo
Find the naugthy shark dadadadada
fsB4bySh4rpcyberx
Boom! THE FLAG appear!
Looks like we just need to delete the txt file to solve this
Thats it, no need to analyse compilcated code we are able to solve this
Alternative Solution
After the finals, I notice we also can save the assembly of Baby_Sharp
by debugging
Set a breakpoint at the Assembly.Load
function:
Click start, then keep step into and step out until it reach Assembly.Load
function, you will see a rawBinary
in the Locals tab:
Right Click it > Save
After that, open the binary with dnSpy you will get the source code of Baby_Sharp
Summary
It was a great CTF, can see it getting harder compare to last year. The challenges all were great and nice, this is the last year we participate for this competition because we graduated and this competition is only for Uni students. Hope in the future, our juniors can participate and win the prize!