I participated in Nullcon 2025, as a core member of the team InfoSecIITR. Our team secured 9th place globally. Here are the Writeups of some challenges I was personally able to solve during the competition.

Misc

Ancient Paper

Challenge Description

We are given an image file along the following description: “I found this ancient artifact stuck in an old machine labeled “29”. But what is its purpose?”

Solution

Initial inspection leads to the conclusion that the image is of an IBM-29 punch card which was carrying our flag as its data.
Punch Card

Decoding the flag

Then I used the following mapping of the IBM-29 punch card to decode the data.

Digits
Alphabets

Which lead to the following text:

1
1337 FORMAT ENO H0LL3R1TH 3NC0D3D F0RTR4N PRINT 1337

And hence we get our flag as:

Flag:

1
ENO{H0LL3R1TH_3NC0D3D_F0RTR4N}

Driving

Challenge Description

We are given a strange video (.mp4) of a “banana driving a Car xD”.

Solution

On extracting the frames of the video using the frames per second rates of 30 fps we get 387 frames.

1
ffmpeg -i driving.mp4 -vf "fps=30" frame_%04d.png

Also the Artist in the video’s metadata looks like a hint for the challenge.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
$ exiftool driving.mp4
ExifTool Version Number : 12.40
File Name : driving.mp4
Directory : .
File Size : 2.2 MiB
File Modification Date/Time : 2025:02:01 14:06:53+05:30
File Access Date/Time : 2025:02:02 18:56:35+05:30
File Inode Change Date/Time : 2025:02:01 14:06:57+05:30
File Permissions : -rwxrwxrwx
File Type : MP4
File Type Extension : mp4
MIME Type : video/mp4
Major Brand : MP4 v2 [ISO 14496-14]
Minor Version : 0.0.0
Compatible Brands : mp42, mp41
Movie Header Version : 0
Create Date : 2025:01:12 15:35:52
Modify Date : 2025:01:12 15:35:52
Time Scale : 90000
Duration : 12.93 s
Preferred Rate : 1
Preferred Volume : 100.00%
Preview Time : 0 s
Preview Duration : 0 s
Poster Time : 0 s
Selection Time : 0 s
Selection Duration : 0 s
Current Time : 0 s
Next Track ID : 3
Track Header Version : 0
Track Create Date : 2025:01:12 15:35:52
Track Modify Date : 2025:01:12 15:35:52
Track ID : 1
Track Duration : 12.90 s
Track Layer : 0
Track Volume : 0.00%
Image Width : 640
Image Height : 480
Graphics Mode : srcCopy
Op Color : 0 0 0
Compressor ID : hvc1
Source Image Width : 640
Source Image Height : 480
X Resolution : 72
Y Resolution : 72
Compressor Name : HEVC Coding
Bit Depth : 24
Video Frame Rate : 30
Matrix Structure : 1 0 0 0 1 0 0 0 1
Media Header Version : 0
Media Create Date : 2025:01:12 15:35:52
Media Modify Date : 2025:01:12 15:35:52
Media Time Scale : 44100
Media Duration : 12.93 s
Media Language Code : eng
Balance : 0
Handler Description : Alias Data Handler
Audio Format : mp4a
Audio Channels : 2
Audio Bits Per Sample : 16
Audio Sample Rate : 44100
Handler Type : Metadata
XMP Toolkit : Image::ExifTool 13.00
Artist : 2+(10*n) for all n>=10
Media Data Size : 2271847
Media Data Offset : 12469
Image Size : 640x480
Megapixels : 0.307
Avg Bitrate : 1.41 Mbps
Rotation : 0

Now on a simple inspection of the frames, one thing that catches our eye is the presence of } in the frame number 383 (indexing from 1).

Frame 383

Then by the Artist hint on inspecting frames on intervals of 10 i.e the following frames,
[103,113,123,133,143,153,163,173,183,193,203,213,223,233,243,253,263,273,283,293,303,313,323,333,343,353,363,373,383]
we find that each of those frames contain one character of the flag in the following order Left Top → Right Top → Right Bottom → Left Bottom → Left Top…, which leads us to our flag.

Flag:

1
ENO{Y0U_4R3_DR1V1N6_M3_CR4ZY}

Powerplay

Challenge Description

An interactive challenge where we are given the python code for the challenge and our job is to trick the server to reveal the flag.

Solution

On initial inspection of the code, we can find the vulnerability in it,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import numpy as np
from secret import flag, quotes

prizes = quotes + ['missingno'] * 4 + [flag] * 24

if __name__ == '__main__':
print('Welcome to our playground for powerful people where you can pump yourself up and get awesome prizes!\n')
player_count = int(input('How many players participate?\n'))
power = np.zeros(player_count, dtype = np.int32)
for i in range(player_count):
power[i] = int(input(f'Player {i}, how strong are you right now?\n'))
ready = False

while True:
print('What do you want to do?\n1) pump up\n2) cash in')
option = int(input())
if option == 1:
power = power**2
ready = True
elif option == 2:
if not ready:
raise Exception('Nope, too weak')
for i in range(player_count):
if power[i] < len(quotes):
print(f'You got an inspiration: {prizes[power[i]]}')
exit()
else:
raise Exception('What?')

The code creates the power as a numpy array of 32 bit signed integer type and then, the pump option lets us square the power array and the check is just if the power value is less than the length of quotes (positive) so one can cause integer overflow in power value to make it negative on squaring but there is one more catch that as the prizes has [flag]*24 we need to specifically make power in the range (-23 to -1) so that we bypass the check and access the prizes array at an index where the flag is present which is one out of the last 24 cells, so we write a brute force script to get such a number.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import numpy as np

start = 1000001
end = 100000000
max_iter = 10

lower_bound = -24
upper_bound = -1

for i in range(start, end + 1):
if i % 100000 == 0:
print(f'{i = }')
a = np.int32(i)
ctr = 0
while ctr < max_iter:
a = a ** 2
ctr += 1
if int(a) > lower_bound and int(a) <= upper_bound:
print(f"Found: i = {i}, iterations = {ctr}, value = {a}")
break

This gives first valid solution as 34716455 1 -15, and on using this as our input we get our flag.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Welcome to our playground for powerful people where you can pump yourself up and get awesome prizes!

How many players participate?
1
Player 0, how strong are you right now?
34716455
What do you want to do?
1) pump up
2) cash in
1
What do you want to do?
1) pump up
2) cash in
2
You got an inspiration: ENO{d0_n0t_be_s0_neg4t1ve_wh3n_y0u_sh0uld_be_pos1t1ve}

Flag:

1
ENO{d0_n0t_be_s0_neg4t1ve_wh3n_y0u_sh0uld_be_pos1t1ve}

Profound thought

Challenge Description

We are given a .png file, and this happened to be the easiest challenge of the CTF.

Solution

A simple LSB steganography challenge, just use zsteg on the file and we get our flag.

1
2
3
4
5
6
7
8
9
10
11
$ zsteg l5b245c11.png
imagedata .. text: "286-0.\t\n\t"
b1,r,lsb,xy .. text: "rzsZA>FCNR^_]\""
b1,r,msb,xy .. file: OpenPGP Public Key
b1,g,msb,xy .. file: OpenPGP Secret Key
b1,rgb,lsb,xy .. text: "ENO{57394n09r4phy_15_w4y_c00l3r_7h4n_p0rn06r4phy} ENO{57394n09r4phy_15_w4y_c00l3r_7h4n_p0rn06r4phy} ENO{57394n09r4phy_15_w4y_c00l3r_7h4n_p0rn06r4phy} ENO{57394n09r4phy_15_w4y_c00l3r_7h4n_p0rn06r4phy} ENO{57394n09r4phy_15_w4y_c00l3r_7h4n_p0rn06r4phy} ENO{57"
b2,r,msb,xy .. file: OpenPGP Secret Key
b2,bgr,msb,xy .. file: OpenPGP Public Key
b3,r,msb,xy .. file: RLE image data, 16888 x 4242, lower left corner: 8321, clear first, 16 color channels
b4,r,lsb,xy .. text: "wYtE$ER\#$DUEFuREEf2E%5"
b4,g,lsb,xy .. text: "#\"5DQ232UT2"

Flag:

1
ENO{57394n09r4phy_15_w4y_c00l3r_7h4n_p0rn06r4phy}

USBnet

Challenge Description

We are given a packet capture of USB packtes.

Solution

On initial analysis of the .pcapng in Wireshark we observe that packet 170 contains the PNG magic bytes of a .png image and it also has the IEND chunk meaning the complete image was transferred in this packet so we just try to reconstruct the image from this packet using Cyberchef which gives the QR of our flag.

Wireshark

Cyberchef

Flag:

1
ENO{USB_ETHERNET_ADAPTER_ARE_COOL_N!C3}

abroad study notes

Challenge Description

We are given a corrupted jpeg image which looks like its data streams are scratched.

Solution

Now from the JPEG documentation we find,
“If a 0xff byte occurs in the compressed image data either a zero byte (0x00) or a marker identifier follows it. Normally the only marker that should be found once the image data is started is an EOI. When a 0xff byte is found followed by a zero byte (0x00) the zero byte must be discarded.”
So on inspecting the given jpeg we find it has ff 07 markers causing the distortion so we just fix them to ff 00 and our jpeg restores to original one.

Final Image

Documentation to refer: jpeg-format-layout

Flag:

1
ENO{o7_t0_4ll_r3pl4c3d_07}