The National Cyber Security Congress (NCSC) is a three-day event that brings together cyber security enthusiasts and experts. The event features a wide range of activities, including workshops, talks, conferences, and two exciting CTF competitions.
As a technical team member, I had the chance to create challenges in Kubernetes, Digital Forensics, Misc, Hardware, and Game Hacking categories.
The NCSC CTF provided a unique opportunity for cyber security enthusiasts to demonstrate their problem-solving abilities, teamwork, and technical proficiency in a fun and competitive environment. Overall, the event was an exciting and rewarding experience for all involved.
In this blog, I will share some write-ups that players have requested!
# Digital Forensics : Mokni & Seals
We got a zip file! I bet that file is a memory dump! So i tried to run the volatility imageinfo plugin but it takes a long time. So I tried to figure out if this file is Linux memory dump or not!
1 | raf@4n6nk8s: strings data.raw | grep "Linux version" |
And yes it’s a Linux Memory dump! I guess its time to start finding/making a profile but let me check that is memory dump can be analyzed with a profile or not!
1 | strings data.raw | grep -i insmod |
Oh! The format is ram! which means that even if we get the correct profile volatility can’t analyze this memory dump! Strings time! The author want us to solve this mem dump using strings !
Let’s try to figure out some command lines! Bash history! Let me show you this trick!
1 | strings data.raw | grep -Ei "@*:~\\$" |
Mmmm nice! now at least we got some commands and we got the user-name and host-name !
From these commands we can understand that the user edit /etc/containers/registries.conf
, then create a secret.txt
. Then logged in with podman using that secret.txt. But we don’t have any information about the content of the creds in these commands!.
We have 2 methods to figure out what’s happen and get the creds!
either running a strings and grepping on CMDLINE=podman login
or grepping on This Is My Secret
and check the lines before and after to see the content!
1 | strings data.raw | grep -Ei "CMDLINE=podman login" |
OR
1 | strings data.raw | grep -Ei -C 2 "This Is My Secret" |
So the creds are 2ecf92b1-83a8-4355-8cb7-8354f4677894:fwO8Q~XTlBmpYoG43J~OusANA_azV02ZS3PuzaXb
But I didn’t think this is a normal creds for dockerhub
or any public famous container registry! Espacially he pulled a container named nginx! I bet that nginx is a customized one and its a trick!
Editing /etc/containers/registries.conf
can confirm that change something! This configuration file used by the container runtime environment on Linux systems. This file specifies a list of container image registries that are trusted by the system and can be used to download container images.
So let’s discover how to get the registry! Pulling nginx means he pulled the latest tag or at least he tried that! So let’s grep on nginx:latest
1 | strings data.raw | grep -Ei "nginx:latest" |
Bingo! qualsk8s.azurecr.io
is the container registry! If you are an Azure guy you can get that from the previous step! when we get this output!
1 | Service principal ID: 2ecf92b1-83a8-4355-8cb7-8354f4677894 |
Ok it’s time to login and pull that image and run it!
1 | docker login qualsk8s.azurecr.io -u 2ecf92b1-83a8-4355-8cb7-8354f4677894 -p fwO8Q~XTlBmpYoG43J~OusANA_azV02ZS3PuzaXb |
After checking the image! I got that it’s a static web page so let’s run it!
1 | docker run --name chall -p 80:80 -d nginx |
After running this container we got this web-page and guess what!! There is a flag icon! Let’s check it !
This flag icon lead you to this link https://pastebin.com/vjFtjMga
which is protected pastebin
how to get that password now! Let’s check the docker history!
1 | mohamed@RafSquare:~$ docker history qualsk8s.azurecr.io/nginx |
And yes!! There is .hidden.txt
that contain a password for sure ! let’s get that password!
1 | docker exec -it 460fc5a977c5 cat .hidden.txt |
We got this pastebin !
This is will lead us to word sheet that contains a hex data!
The hex data seams introducing spaces and tabs! I don’t care let’s decode it and see what’s will be!
I’ll try to convert it to a file using xxd
and import that file to dcode.fr
1 | xxd -r -p hex.txt > result.txt |
Yeah it’s a file that contains a lot of spaces! Its whitespaces
Bingo! This is the flag Securinets{208e5976c9d654e47d73ef44cabc272d}
# Digital Forensics: Recover my Work 😦
We got a file named memory.dmp
. It’s a memory dump for sure! Let’s run imageinfo plugin to get our profile!
1 | vol.py -f memory.dmp imageinfo |
But sadly we got this type of outpût! I bet that the suggested profile (in case it exists) will be wrong! Let’s know the exact profile with my own way! Let’s run strings the file and grep “service pack”
1 | Windows 7 Professional, 64-bit Service Pack 1 (Build 7601) |
Cool, This build and service pack is supported by Win7SP1x64
! I guess that imageinfo plugin got problem because the iso is customizated !
Let’s start checking our memory dump! First thing should be checked is the running processes! I’ll use pstree for that thing!
MMMM intersting! Found chrome, firefox, outlook and RDP opened! what a trip! After digging on chrome history and firefox, I found rabbit holes and some links to wordlists! Bad Author (Me).
Checking the consoles plugin too. Another rabbit hole! rabbit hole everywhere!
It’s time to check and figure out that outlook process!
Outlook save the mails as pst or ost files! These files contains the mails data such as the message, attachements, sender/receiver information!
So let’s try to extract these files !
1 | vol.py -f memory.dmp --profile=Win7SP1x64 filescan | grep -i pst$ |
After extracting the files using dumpfiles
plugin and making sure that the files are safe and not corrupted. I opened them using a pst online viewer!
The 1st mail i got it is from Adam! He send to me a zip file inside mega storage after our online meeting! I bet that zip is protected with that boring password
Ah my bad! The mega too is protected! We need the encryption key!
Let’s check the other mail! and as expected Adam forget to share the decryption key of the mega link to open the zip!
Bingo we got the zip! But it’s protected! Tried bruteforcing the password. But I failed!
Mmmm Let’s think a little bit! The author said that he used to use the same password! We need to find another usage of that password to open the zip and get the flag!
And It’s time to investigate the mstsc.exe
or let’s just say the RDP! Remote Desktop?? Mmmm how to extract information from that thing?
Let’s talk brievly! Extracting the memory part used by RDP can save our life! I’ll try to recover what the user see when he used RDP!!!
Yes this possible dear reader, Don’t worry You’ll see how! Just make sure that you have Gimp
Let’s now extract the memory part used by RDP process using the memdump
plugin in volatility!
1 | vol.py -f memory.dmp --profile=Win7SP1x64 memdump -p 360 -D . |
After getting that file! Let’s change its extension from .dmp
to .data
and open that file with gimp!
Now it’s time for focusing! Yeah focusing will save your time! Many people can’t figure or recover images quickly! But believe me focusing on the noise will help you recover the data in a short time!
After 4 mins of playing with offset and width i got it!
Check the offset and the width!
Ohhh man! The user was opened https://ctf.securinets.tn (check it for know more information about our CTFs events xD ) and tried to secure a zip file with that shitty password!
Finally This is the flag Securinets{R3M0tE_DeSKtOp_1s_FunNy_!_!}
# Hardware: Liquid Display
We got an image and a data file!
The image contains an LCD 16x2 SPI connected to something called WOKWI LOGIC
After some searching I got that this component is a logic analyzer. But wait!! What is that??
A logic analyzer is an electronic instrument that captures and displays multiple signals from a digital system or digital circuit. A logic analyzer may convert the captured data into timing diagrams, protocol decodes, state machine traces…
As my friend said this is the hardware wireshark xD
So we can understand now that this circuit capture the signals of each pin of the LCD and the data file is the capture file that contains the files!
So after some searching we get how to open that data file (which is a ASCII text file, I didn’t recommend to analyse it as text file).
You can check this link to get more information about the logic analyzer!
PulseView is an open source Logic Analyzer GUI belongs to The sigrok project that aims at creating a portable, cross-platform, Free/Libre/Open-Source signal analysis software suite that supports various device types
I’ll use that Logic Analyzer GUI for this challenge!
Import the file data in this way! and let’s the show begin!
A lot of fun wait us right?! Don’t worry man! This is not that super hard! Just all what we need to do now is to simulate these signals manually! These signals are sent to the LCD and that LCD display something (It should be the flag!). So we need to understand how LCD works! I recommend to read the datasheet this thing!
I will not re-write what the datasheet said! so read it! You should understand how the LCD dispaly chars!
Also don’t forget to check the instruction table and understand how these instruction works!
Let’s just take an example of the 1st instruction! The Clear Display
. This instruction clear all the data and return the cursor to the original status! Ah yeah of course you must to understand that LCD have cursors, display modes …
After understand how this LCD works it’s time to check how characters are written on that LCD!
This table will help us to convert the signals to chars and recover our flag!
After reading the datasheet. You will understand that the D0 (that correspond to RS pin) can help you to understand when the display device clear the chars and reset the cursor or write something on the screen!
If you take a closer look you’ll get it! The guy who write the program write something and clear it then write another thing then delete it and so on!
So now it’s time to check the write blocks and check what that guy try to do! After reversing all the block i got the flag! It’s on the last block! Let me show you how I recover that data!
Using the characters table you can recover the flag! Mapping the signal and the Big table lead you to the flag!
And This is will be our flag! Securinets{LcD_1s_H4rD_!:(}
# Game Hacking: Platformer:
In this challenge we have a game made by unity! It’s 2D platformer game where the player can jump and run in the map! But where is the flag??
Mmmmm my sixth sense told me that the flag is hidden somewhere in the map and the player can’t reach it! Hack Time!!!
Let’s change the player ability and let him can move in any place we want it. By disabling the collision and physics mechanism! Good bye gravitiy,Rigidbody,collisions!
To do this let’s open Assembly-CSharp.dll
located in Platformer_Data/Managed
folder.
Wow! This is the player controller code! that allow the player to move and the animation to be played and responsable for jumping and detect ground detection to deny player to jump many times on the air!
Boring mechanism!! Let me change this shitty code by mine! I’ll let the player move to any point he want!
You can copy that code
1 | private void Start() |
After applying the changes our player can now go to any place we want! But i didn’t find the flag! Oh god! This is what i found!
I checked all the map and sadly nothing interesting 😦
Let’s check the files again! Maybe we will find something!
Oh man look here! I found that there is levels on this game! Let me back to the decompiled code!
Oh no! The game is opened on the 2nd level! Let’s change this room to “Level1” and Play again! And yes It’s a new Level
After moving right and left, I found the flag!
And Yes We did it! Just move right and left and collect the letters ! and you’ll get this one ! Wrap it in Securinets{}
Flag: Securinets{Gam1ng_AnD_L0VE}
# Misc: Full Difference
Full Difference, The difference will make a difference! What is that thing ?? we got 2 images with different type but same picture!
Thinking a little bit can we understand what the author means! Full difference! It means that the pixels should be different a full difference! which means the Red,Green and Blue channels should be differents!
“Will make a difference!” After extracting the different pixels we should calculate the difference between each channels ?? Mmmmm makes sense!
1 | from PIL import Image |
So the logic behind that code is to extract the pixels that have a full difference then calculate the difference between them. After that we convert the numbers to chars (should be printable!)
This expression will give us the different pixels
1 | pix1[i,j] != pix2[i,j] and pix1[i,j][0] != pix2[i,j][0] and pix1[i,j][1] != pix2[i,j][1] and pix1[i,j][2] != pix2[i,j][2] |
In addition this expression collect the result of calculation the diff between the 2 pixels of each image!
1 | data.append(chr(abs(pix1[i,j][k] - pix2[i,j][k]))) |
Then it’s time to convert it to printable data!
1 | for char in data: |
Running this code will give you this base64 encoding! dGgxNV8xNV9zaDB1TGRfYjNfaDRyRA==
Bingo we got this statement! th15_15_sh0uLd_b3_h4rD
. Let’s wrap it in Securinets! Oh nooo! Didn’t work. I guess the challenge is not finished!
We have 2 images! PNG and JPEG. Mmmm Let’s try to do some steganography on these images using the password/key that we got previously.
The 1st idea in my mind is to try steghide! Nothing else! Let’s try it
YEEES MAAN! We got a flag.zip file! But wait it’s protected again 😦. I tried to brute force that thing using rockyou but failed !!! What is going on here! The flag is inside that zip what we should to do now!
Let’s extract more information about the zip using 7z
utility
1 | 7z l -slt flag.zip |
Nice the flag.txt size is 40 bytes! So our flag is 40-chars strings! The encryption method is ZipCrypto
!
Bingoo! This method is vulnerable! Let’s check how can we get our flag!
After some searching we found a repository talking about “Crack legacy zip encryption with Biham and Kocher’s known plaintext attack.”
I recommend to check this repo and understand how this attack can be manipulated and how to install that tool!
Let’s now start downloading the tool
1 | wget https://github.com/kimci86/bkcrack/releases/download/v1.5.0/bkcrack-1.5.0-Linux.tar.gz |
We need to recover the internal key that will allow us to extract the file!
The attack requires at least 12 bytes of known plaintext. At least 8 of them must be contiguous. The larger the contiguous known plaintext, the faster the attack. In our case we have a flag.txt that contains Securinets{*}
In our case we know 11 chars ( Securinets{
) and the last char }
Let’s recover our key now!
1 | echo -n "Securinets{" > plaintext.txt |
The attack requires the plain text content and in case there is another seperate bytes we can specify the offset and the byte value in hex
Now time to work !
1 | bkcrack-1.5.0-Linux/bkcrack -C flag.zip -c flag.txt -p plaintext.txt -x 39 7d |
We got the keys ! 184a904b d4557686 2222c7f2
. Now we can decipher/decrypt the file! which means extract it from the zip!
1 | bkcrack-1.5.0-Linux/bkcrack -C flag.zip -c flag.txt -k 184a904b d4557686 2222c7f2 -d my_flag.txt |
Bingo We got the flag!!! How 2 images can hide data like that!
Flag: Securinets{Z1p_CRyPt0_&_5t3gH1d3_1s_BAd}
# Kubernetes challenge serie
NCSC’2023 consider the 1st tunisian CTF competition that have a whole Kubernetes Category! These are oriented for beginners/Intermediate users to consodilate their basic knowledge in Kubernetes!
# Kubernetes: Secrets:
In this challenge we get an IP and the author told us that he have a secret in the cluster!
Visiting the IP on the browser will lead us to the author page! This is not our objective!
We need to access to the API-Server. Let’s check the default port 6443
1 | curl -sk https://20.169.73.19:6443/version |
And this request is failed! Mmmm The author change the default api-server?? Nmap time! Let’s scan that IP!
After checking the IP we get that port opened 7443!
And Yes! We got a response! It’s KUBERNETES TIME!!
Let’s check what permission we have as an anonymous users! To be honest I’ll try to check if I can got namespaces Or secrets first! Let me check that!
1 | curl -sk https://20.169.73.19:7443/api/v1/namespaces | grep '"name": "' |
And we got a list of namespaces! This is cool! We have task1,task2,task3 and task4
namespaces! I bet that each challenge is in single namespace! This is Great!
Hummm We need secrets and this is the 1st challenge! So We are sure that we can list the secrets in the task1
namespace!
1 | curl -sk https://20.169.73.19:7443/api/v1/namespaces/task1/secrets |
Bingo We got the Secrets List! We are on the right way!
Flag : Securinets{S3crEts_Ar3_S0_CriT1c4LL}
We got a message! We must check it for sure!
Look what we got here !
1 | Look here YOU will need this one believe me!!! |
# Kubernetes: Pody:
After getting the secrets we can move to the next challenge that named Pody
In this challenge the author told us that the container is inside a pod! So how can we get inside that pod? Thinking a little bit we didn’t get any solution expect opening a shell session inside the pod!
Kubectl Are you there?? Yes! It’s Kubectl time! I love to work with kubectl I will not waste my time curling endpoints 😃 So I’ll make my kubeconfig file for this challenge!
When I checked the secrets in the previous challenge I got the certificate authority Certifcate. And of course don’t forget the token that we got!
This token is used for authentication and authorization in kubernetes. This authorization is occur on the api-server level not the etcd!
Let’s make our kubeconfig! But wait! In case you don’t have kubectl, it’s time to install it! You can follow this guide to install it
1 | apiVersion: v1 |
This kubeconfig file will allow us to authenticate to the api-server using kubectl utility without wasting time specifying the token and other stuff!
Assume that you save that file in name ncsc-k8s.conf
. Let’s export the KUBECONFIG env var.
1 | $ export KUBECONFIG=ncsc-k8s.conf |
Bingo !! We got access and everything is ok until now. Let’s describe the pod and check what we have first before getting a shell !
1 | kubectl describe pod web-app -n task2 |
Wow there is flag.txt
file inside the pod! Let’s be more accurate! The flag is in /etc/nginx/flag.txt
Let’s get a shell or run a command from the pod using the kubectl exec
command!
Yes we got the flag! And another message: Your current token is enough!
I tried to delete the flag! But as expected the author make the pod Read-only file system
Flag : Securinets{Ex3c_1s_DAnGer0uS_B3_C4r3fUL}
# Kubernetes: Hidden? :
We still have the same token! Our kubectl works fine. So no worries we can do it!
In this challenge the flag is hidden?? But how!? Let’s check first what can we do in our task3
namespace
1 | kubectl get service -n task3 |
We can access to services! In case you don’t know what is service I recommend to check this page. As we understand, There is a pod inside task3 namespace but we don’t have any access to it 😦!. No worries we still have services! This service as we can see its attached to that pod. Let’s get our flag!
After a little bit of thinking, I got an idea! Let’s access to the service from our previous pod!
YES MAN! pods and services can communicate between each others
Ok let’s do it then, we have the service internal IP and we can run curl command inside our previous pod!
Bingo! Flag: Securinets{K8s_S3rV1cEs_ArE_P0wErFull}
And as Usual! another token for the next challenge :
1 | In The Next Challenge You will Need This one! |
# Kubernetes: Special :
After getting the new token it’s time to edit the kubeconfig file! Just replace the old toke by the new one! To work with kubectl correctly!
Something Special?? What a special? Everything in Kubernetes is SO Special!! So no worries, We can deal with that kind of things!
Talking about something special take me to think about what we can call it Custom Resources Definition
in Kubernetes!
In Kubernetes, a custom resource is an extension of the Kubernetes API that allows you to define your own custom resources with their own custom controllers.
A custom resource definition (CRD) is used to create a new custom resource type in Kubernetes. A CRD defines the structure and behavior of the new custom resource, including its name, attributes, and API endpoints. Once a CRD is defined, instances of the custom resource can be created and managed using Kubernetes tools like kubectl and the Kubernetes API.
1 | kubectl api-resources |
But wait! I am right! There is an api-group and a custom resource called ncscctfs!
Now it’s time to get the flag!
1 | kubectl get ncscctfs -n task4 |
And yes there is a ncscctf resource named flag! Let’s describe that thing and get the flag!
1 | kubectl describe ncscctfs flag -n task4 |
Flag: Securinets{CuSt0m_REs0urcEs_ArE_P0wErFul}
# Final Words
In conclusion, the success of this event and competition is due in no small part to the dedication and hard work of the technical team. Their expertise and professionalism were instrumental in overcoming the various challenges we faced throughout the event lifecycle, and their tireless efforts ensured that we delivered a product that met the highest standards of quality and performance. On behalf of the team, I would like to express our sincere gratitude to our technical colleagues for their unwavering commitment to the project, and for their invaluable contributions to its success