Recap UIUCTF 2023 - Team 42Roma
Corny Kernel Challenge
Available files
- pwnymodule.c
Walktrough of my solution (which is pretty bad, actually):
- To connect to the machine, use the given command trough socat:
socat file:$(tty),raw,echo=0 tcp:corny-kernel.chal.uiuc.tf:1337
After checcking all the files available on the machine, I noticed a file called pwnymodule.ko.gz in the /root directory. This file turned out to be a Kernel Module and could be live-mounted live on the kernel (which is the purpose of this challenge).
- To mount the kernel module, run this command:
insmod pwnymodule.ko.gz
- Once the module is mounted, I could check the kernel logs by running this command:
dmesg
I noticed that the logs displayed the first part of the flag. Then, I tried to unmount the module to see if anything would change.
- To unmount the kernel module, run this command:
rmmod pwnymodule.ko.gz
After that, I ran the dmesg command again, and boom, the second part of the flag appeared. So now i have both the first and second parts of the flag.
[flag] = uiuctf{m4ster_k3rNE1_haCk3r}**
[extra] Initially, I thought that to obtain the flag, I had to remove the file “init” located in the root directory. Well, that turned out to be false, but I wanted to mention it anyway.
[more info]
I’ll add some links once i find some good writeups.
Group Project Challenge
Available files
- chal.py
Walktrough of my solution (which is also pretty bad, actually)
The pourpose of this challenge was to use the group theory maths and (from what i understood) the Lagrange’s theorem.
The basic idea of the challenge is that you get the flag encrypted in c
, and to decrypt it, you need to force the final result to be 1, which can be achieved by choosing the value of k
.
If we examine the given code, we can see that someting happens when they check the value of k
, particularly when k = p - 1
.
In facts if k = p - 1
that means that after some algebraic operations and regardless the values of a and b, the exponent will always be a multiple of p - 1
.
It can be rappresent as: (p - 1) * a * b
.
Thanks to this, we can force S to become 1, according to group theory, specifically Lagrange’s theorem we have:
g^(p-1) \mod p = 1
Because the group has order p - 1.
In this case if we force k = p - 1 we get:
- S = g^(a * b * k) \mod p
- = g^(a * b * (p-1)) \mod p
- = (g^(p-1))^(a * b) \mod p
- = 1^(a * b) \mod p
- = 1
let’s see my specific case
- To connect to the machine, use the given command through nc:
nc group.chal.uiuc.tf 1337
This is an example run where we choose k = p - 1
== proof-of-work: disabled ==
[$] Did no one ever tell you to mind your own business??
[$] Public:
[$] g = 2
[$] p = 123550563549747490323249224338990831394408051910991173735163283881290684423205314178830285306348019042127955168105946500849992929461306092791621619837542285563398201676541157481311715881882861461771386482605615740025776697545491552863159651061852777469798729628428268198459861272156418110367594843550743172627
[$] A = 65577571120208990271365027516450216371270903165222652333226308026118839957307253811739762319146299166577506464436196113831495358295414487806901676838911064532591294232195578279790096365970082755598631058932069218146713938218135724446862334581120056129894062668713758501672608059247131378197566441717688794719
[$] Choose k = 123550563549747490323249224338990831394408051910991173735163283881290684423205314178830285306348019042127955168105946500849992929461306092791621619837542285563398201676541157481311715881882861461771386482605615740025776697545491552863159651061852777469798729628428268198459861272156418110367594843550743172626
[$] Ciphertext using shared 'secret' ;)
[$] c = 31383420538805400549021388790532797474095834602121474716358265812491198185235485912863164473747446452579209175051706
If we try multiples times to put k = p - 1 we always get the same c value. This is because each time, S is reduced to 1, resulting in the same cipher value.
We can now decypher the flag using the provided file for understanding the cryptography used.:
S = 1
key = hashlib.md5(long_to_bytes(S)).digest()
cipher = AES.new(key, AES.MODE_ECB)
#Then we go from number to encrypted bytes. Note the 48 comes from the fact that 16 * 3 = 48.
c = 31383420538805400549021388790532797474095834602121474716358265812491198185235485912863164473747446452579209175051706
c = c.to_bytes(48, 'big')
#Finally we decrypt the string and remove the padding (encryption stuff).
p = cipher.decrypt(c)
p = unpad(p, 16)
#Voilà, our flag.
print(p)
[flag] = uiuctf{brut3f0rc3_a1n’t_s0_b4d_aft3r_all!!11!!}**
[extra] (҂◡_◡) ᕤ
[more info]
I’ll add some links once i find some good writeups.
Special thanks to the 42Roma Cybersecurity Club for their time and support, looking forward to the next CTFs!