LA CTF 2024

This is the second time I have participated in the LA CTF 2024 event

https://ctftime.org/event/2102



CHALL’S SOLVED

CategoryChallenge
CRYPTOGRAPHYvery-hot
REVERSE ENGINEERINGshattered-memories
REVERSE ENGINEERINGaplet321

very-hot

Description

Author: Red Guy
I didn’t think that using two primes for my RSA was sexy enough, so I used three.

src.py
out.txt

Provided chall source src.py

src.py
 1from Crypto.Util.number import getPrime, isPrime, bytes_to_long
 2from flag import FLAG
 3
 4FLAG = bytes_to_long(FLAG.encode())
 5
 6p = getPrime(384)
 7while(not isPrime(p + 6) or not isPrime(p + 12)):
 8    p = getPrime(384)
 9q = p + 6
10r = p + 12
11
12n = p * q * r
13e = 2**16 + 1
14ct = pow(FLAG, e, n)
15
16print(f'n: {n}')
17print(f'e: {e}')
18print(f'ct: {ct}')

And out.txt

out.txt
n: 10565111742779621369865244442986012561396692673454910362609046015925986143478477636135123823568238799221073736640238782018226118947815621060733362956285282617024125831451239252829020159808921127494956720795643829784184023834660903398677823590748068165468077222708643934113813031996923649853965683973247210221430589980477793099978524923475037870799
e: 65537
ct: 9953835612864168958493881125012168733523409382351354854632430461608351532481509658102591265243759698363517384998445400450605072899351246319609602750009384658165461577933077010367041079697256427873608015844538854795998933587082438951814536702595878846142644494615211280580559681850168231137824062612646010487818329823551577905707110039178482377985

The provided Python script is implementing a basic RSA encryption scheme with a twist. It generates a prime number p and ensures that p+6 and p+12 are also prime, assigning these to q and r respectively. The product of p, q, and r forms the RSA modulus n. The public exponent e is set to 2^16 + 1, a common choice in RSA systems. The script then converts a secret flag into a long integer and encrypts it using RSA, resulting in the ciphertext ct. The output file contains the values of n, e, and ct, which are part of the public key and the encrypted message in the RSA encryption scheme.

Final solver.py

solver.py
 1from sympy import nextprime, root
 2from Crypto.Util.number import long_to_bytes
 3
 4def factorize(n):
 5    p = nextprime(int(root(n, 3)))
 6    while n % p != 0:
 7        p = nextprime(p)
 8    n //= p
 9    q = nextprime(p)
10    while n % q != 0:
11        q = nextprime(q)
12    r = n // q
13    return p, q, r
14
15def decrypt(ct, p, q, r, e):
16    phi = (p - 1) * (q - 1) * (r - 1)
17    d = pow(e, -1, phi)
18    return pow(ct, d, n)
19
20n = 10565111742779621369865244442986012561396692673454910362609046015925986143478477636135123823568238799221073736640238782018226118947815621060733362956285282617024125831451239252829020159808921127494956720795643829784184023834660903398677823590748068165468077222708643934113813031996923649853965683973247210221430589980477793099978524923475037870799
21e = 65537
22ct = 9953835612864168958493881125012168733523409382351354854632430461608351532481509658102591265243759698363517384998445400450605072899351246319609602750009384658165461577933077010367041079697256427873608015844538854795998933587082438951814536702595878846142644494615211280580559681850168231137824062612646010487818329823551577905707110039178482377985
23
24p, q, r = factorize(n)
25flag = decrypt(ct, p, q, r, e)
26print(long_to_bytes(flag))

To decrypt an RSA-encrypted message. It first factorizes the RSA modulus n into its prime factors p, q, and r using a function called factorize. This function exploits the fact that p, q, and r are close together, which allows for efficient factorization. Then uses these factors to compute the RSA private key and decrypt the ciphertext ct using a function called decrypt. The decrypted message is converted back to bytes and printed out, revealing the original message.

running
$ python3 solver.py
b'lactf{th4t_w45_n0t_so_53xY}'
FLAG

lactf{th4t_w45_n0t_so_53xY}


shattered-memories

Description

Author: aplet123
I swear I knew what the flag was but I can’t seem to remember it anymore… can you dig it out from my inner psyche?

shattered-memories

Provided executable binary file ./shattered-memories

And here’s the decompiled of the main function

decompiled main
undefined8 main(void)

{
  int iVar1;
  size_t sVar2;
  undefined8 uVar3;
  char local_98 [8];
  char acStack_90 [8];
  char acStack_88 [8];
  char acStack_80 [8];
  char acStack_78 [108];
  int local_c;
  
  puts("What was the flag again?");
  fgets(local_98,0x80,stdin);
  strip_newline(local_98);
  sVar2 = strlen(local_98);
  if (sVar2 == 0x28) {
    local_c = 0;
    iVar1 = strncmp(acStack_90,"t_what_f",8);
    local_c = local_c + (uint)(iVar1 == 0);
    iVar1 = strncmp(acStack_78,"t_means}",8);
    local_c = local_c + (uint)(iVar1 == 0);
    iVar1 = strncmp(acStack_80,"nd_forge",8);
    local_c = local_c + (uint)(iVar1 == 0);
    iVar1 = strncmp(local_98,"lactf{no",8);
    local_c = local_c + (uint)(iVar1 == 0);
    iVar1 = strncmp(acStack_88,"orgive_a",8);
    local_c = local_c + (uint)(iVar1 == 0);
    switch(local_c) {
    case 0:
      puts("No, that definitely isn\'t it.");
      uVar3 = 1;
      break;
    case 1:
      puts("I\'m pretty sure that isn\'t it.");
      uVar3 = 1;
      break;
    case 2:
      puts("I don\'t think that\'s it...");
      uVar3 = 1;
      break;
    case 3:
      puts("I think it\'s something like that but not quite...");
      uVar3 = 1;
      break;
    case 4:
      puts("There\'s something so slightly off but I can\'t quite put my finger on it...");
      uVar3 = 1;
      break;
    case 5:
      puts("Yes! That\'s it! That\'s the flag! I remember now!");
      uVar3 = 0;
      break;
    default:
      uVar3 = 0;
    }
  }
  else {
    puts("No, I definitely remember it being a different length...");
    uVar3 = 1;
  }
  return uVar3;
}

The function first prompts the user for input. It then checks if the length of the input is 40 characters (0x28 in hexadecimal). If the length is correct, it compares segments of the input with predefined strings. If the length is not correct will returns 1. The function seems to be checking if the input matches a specific format or pattern. The correct input would likely result in the function returning 0.

Base on strcmp they start with the strings t_what_f, t_means}, nd_forge, lactf{no, and orgive_a. Put into correct order.

running
$ ./shattered-memories
What was the flag again?
lactf{not_what_forgive_and_forget_means}
Yes! That's it! That's the flag! I remember now!
FLAG

lactf{not_what_forgive_and_forget_means}


aplet321

Description

Author: kaiphait
Unlike Aplet123, Aplet321 might give you the flag if you beg him enough.

Dockerfile
aplet321

Provided executable binary file ./aplet321

And here’s the decompiled of the main function

decompiled main
undefined8 main(void)

{
  int iVar1;
  size_t sVar2;
  char *pcVar3;
  int iVar4;
  int iVar5;
  char local_238;
  char acStack_237 [519];
  
  setbuf(stdout,(char *)0x0);
  puts("hi, i\'m aplet321. how can i help?");
  fgets(&local_238,0x200,stdin);
  sVar2 = strlen(&local_238);
  if (5 < sVar2) {
    iVar4 = 0;
    iVar5 = 0;
    pcVar3 = &local_238;
    do {
      iVar1 = strncmp(pcVar3,"pretty",6);
      iVar5 = iVar5 + (uint)(iVar1 == 0);
      iVar1 = strncmp(pcVar3,"please",6);
      iVar4 = iVar4 + (uint)(iVar1 == 0);
      pcVar3 = pcVar3 + 1;
    } while (pcVar3 != acStack_237 + ((int)sVar2 - 6));
    if (iVar4 != 0) {
      pcVar3 = strstr(&local_238,"flag");
      if (pcVar3 == (char *)0x0) {
        puts("sorry, i didn\'t understand what you mean");
        return 0;
      }
      if ((iVar5 + iVar4 == 0x36) && (iVar5 - iVar4 == -0x18)) {
        puts("ok here\'s your flag");
        system("cat flag.txt");
        return 0;
      }
      puts("sorry, i\'m not allowed to do that");
      return 0;
    }
  }
  puts("so rude");
  return 0;
}

It first prints a greeting message, then reads a line of input from the user. If the input length is more than 5 characters, it checks for the occurrence of the words “pretty” and “please” in the input. If the word “please” is found, it further checks for the word “flag”. If “flag” is also found and the sum of the occurrences of “pretty” and “please” equals 54 (0x36 in hexadecimal) and the difference between the occurrences of “pretty” and “please” equals -24 (-0x18 in hexadecimal), it prints a message and executes the command cat flag.txt. If these conditions are not met, it prints a refusal message. If the input length is 5 characters or less, it prints a message indicating that the user is being rude. The function returns 0 before it ends.

Final solver.py

solver.py
 1from pwn import *
 2
 3def exploit(io):
 4    io.recvuntil(b"how can i help?")
 5
 6    payload = b"please" * 39
 7    payload += b"pretty" * 15
 8    payload += b"flag"
 9    io.sendline(payload)
10
11    # output = io.recvall()
12    # print(output)
13    io.interactive()
14
15if __name__ == "__main__":
16    context.update(log_level='debug')
17    try:
18        io = remote('chall.lac.tf', 31321)
19        exploit(io)
20    except:
21        io = process('./aplet321')
22        exploit(io)

In order to exploit. The payload consists of the word “please” repeated 39 times, “pretty” repeated 15 times, and “flag”. The sum of the occurrences of “pretty” and “please” equals 54 (0x36 in hexadecimal) and the difference between the occurrences of “pretty” and “please” equals -24 (-0x18 in hexadecimal). After sending the payload.

running
$ python3 solver.py
[+] Opening connection to chall.lac.tf on port 31321: Done
[DEBUG] Received 0x22 bytes:
    b"hi, i'm aplet321. how can i help?\n"
[DEBUG] Sent 0x149 bytes:
    b'pleasepleasepleasepleasepleasepleasepleasepleasepleasepleasepleasepleasepleasepleasepleasepleasepleasepleasepleasepleasepleasepleasepleasepleasepleasepleasepleasepleasepleasepleasepleasepleasepleasepleasepleasepleasepleasepleasepleaseprettyprettyprettyprettyprettyprettyprettyprettyprettyprettyprettyprettyprettyprettyprettyflag\n'
[*] Switching to interactive mode

[DEBUG] Received 0x14 bytes:
    b"ok here's your flag\n"
ok here's your flag
[DEBUG] Received 0x35 bytes:
    b"lactf{next_year_i'll_make_aplet456_hqp3c1a7bip5bmnc}\n"
lactf{next_year_i'll_make_aplet456_hqp3c1a7bip5bmnc}
[*] Got EOF while reading in interactive
$
[*] Interrupted
[*] Closed connection to chall.lac.tf port 31321
FLAG

lactf{next_year_i'll_make_aplet456_hqp3c1a7bip5bmnc}