UTCTF 2024

https://ctftime.org/event/2302



CHALL’S SOLVED

CategoryChallenge
BeginnerBasic Reversing Problem
BeginnerOff-Brand Cookie Clicker
CryptographyRSA-256
ForensicsContracts

Beginner

Basic Reversing Problem

Overview

Author: By Khael (@malfuncti0nal on discord)

Description: So many function calls… but are they that different?

baby-rev

In this basic reverse challenge we are given a binary file and after debugging it, there are many functions that we get afterwards

$ gdb ./baby-rev
gef➤  info functions
All defined functions:

Non-debugging symbols:
0x0000000000001000  _init
0x0000000000001040  __cxa_finalize@plt
0x0000000000001050  __stack_chk_fail@plt
0x0000000000001060  _start
0x0000000000001090  deregister_tm_clones
0x00000000000010c0  register_tm_clones
0x0000000000001100  __do_global_dtors_aux
0x0000000000001140  frame_dummy
0x0000000000001149  l1
0x0000000000001178  l2
0x00000000000011a7  l3
0x00000000000011d6  l4
0x0000000000001205  l5
0x0000000000001234  l6
0x0000000000001263  l7
0x0000000000001292  l8
0x00000000000012c1  l9
0x00000000000012f0  l10
0x000000000000131f  l11
0x000000000000134e  l12
0x000000000000137d  l13
0x00000000000013ac  l14
0x00000000000013db  l15
0x000000000000140a  l16
0x0000000000001439  l17
0x0000000000001468  l18
0x0000000000001497  l19
0x00000000000014ad  keygen
0x00000000000014eb  main
0x0000000000001510  __libc_csu_init
0x0000000000001580  __libc_csu_fini
0x0000000000001588  _fini

We can use Ghidra/IDA to see the decompiled version

decompiled function: l1
void l1(undefined *param_1)

{
  *param_1 = 0x75;
  l2(param_1 + 1);
  return;
}

void l2(undefined *param_1)

{
  *param_1 = 0x74;
  l3(param_1 + 1);
  return;
}

void l3(undefined *param_1)

{
  *param_1 = 0x66;
  l4(param_1 + 1);
  return;
}

...

void l18(undefined *param_1)

{
  *param_1 = 0x7d;
  l19(param_1 + 1);
  return;
}

So.. if we decode hex value of l1l18 functions one by one we can get the part of flag

$ python3
>>> bytes.fromhex('75 74 66 6c 61 67').decode('utf-8')
'utflag'
>>>

Final solver

>>> vals = [0x75, 0x74, 0x66, 0x6c, 0x61, 0x67, 0x7b, 0x69, 0x5f, 99, 0x34, 0x6e, 0x5f, 0x72, 0x33, 0x76, 0x21
, 0x7d]
>>>
>>> print(''.join(chr(value) for value in vals))
utflag{i_c4n_r3v!}
FLAG

utflag{i_c4n_r3v!}


Overview

Author: By Khael (@malfuncti0nal on discord)

Description: I tried to make my own version of cookie clicker, without all of the extra fluff. Can you beat my highscore?

http://betta.utctf.live:8138/

From the provided link, we have to exceed 10,000,000 clicks.



It displays this count on an HTML element with the id clickCount. An event listener is added to an HTML element with the id cookieImage.

When this element is clicked, the count is incremented & the new count is displayed and stored in the local storage. If the count reaches or exceeds 10,000,000, a POST request is sent to the /click endpoint with the count as the body of the request.

solver.py
 1import requests
 2
 3url = "http://betta.utctf.live:8138/click"
 4
 5headers = {
 6    'Content-Type': 'application/x-www-form-urlencoded'
 7}
 8
 9data = {
10    'count': 10000000
11}
12
13response = requests.post(url, headers=headers, data=data)
14
15print(response.json())
$ python3 solve.py
{'flag': 'Wow, you beat me. Congrats! utflag{y0u_cl1ck_pr3tty_f4st}'}
FLAG

utflag{y0u_cl1ck_pr3tty_f4st}



Cryptography

RSA-256

Overview

Author: By Jeriah (@jyu on discord)

Description: Based on the military-grade encryption offered by AES-256, RSA-256 will usher in a new era of cutting-edge security… or at least, better security than RSA-128.

vals.txt

From the given source. The values are:

  • N: the modulus for both the public and private keys
  • e: the public exponent
  • c: the ciphertext want to decrypt
N = 77483692467084448965814418730866278616923517800664484047176015901835675610073
e = 65537
c = 43711206624343807006656378470987868686365943634542525258065694164173101323321

To solve this, we would factorize N into two prime numbers, p and q. This is often the most time-consuming step, especially when N is a large number. Alternatively, we can use online factorization tools such as FactorDB. Just enter the number and it will return the factors.

After we get the factors of N. Here’s the optional step that might check whether p and q are indeed factors of N.

N = 77483692467084448965814418730866278616923517800664484047176015901835675610073
p = 1025252665848145091840062845209085931
q = 75575216771551332467177108987001026743883

if p * q == N:
    print("p * q == N")
else:
    print("p * q != N")

Final solver

solver.py
 1from Crypto.Util.number import inverse, long_to_bytes
 2
 3# given values
 4N = 77483692467084448965814418730866278616923517800664484047176015901835675610073
 5e = 65537
 6c = 43711206624343807006656378470987868686365943634542525258065694164173101323321
 7
 8# factorized (N) with factordb.com
 9p = 1025252665848145091840062845209085931
10q = 75575216771551332467177108987001026743883
11
12phi = (p - 1) * (q - 1)
13d = inverse(e, phi)
14m = pow(c, d, N)
15
16message = long_to_bytes(m)
17print(message)
$ python3 solve.py
b'utflag{just_send_plaintext}'
FLAG

utflag{just_send_plaintext}



Forensics

Contracts

Overview

Author: By Samintell (@samintell on discord)

Description: Magical contracts are hard. Occasionally, you sign with the flag instead of your name. It happens.

document.pdf

Given the forensics challenge, and provided PDF document

Trying various ways to search flags, such as searching for all uppercase letters, but it didn’t work

$ pdftotext file.pdf output.txt
$ grep -o '[A-Z]' output.txt

So, after searching for PDF forensics tools, we found a tool that was suitable for solving this by extracting images from PDF using pdfimages

$ mkdir ext && pdfimages -all document.pdf out && mv out* ext

$ ls ext/
out-000.jpg  out-002.jpg  out-004.png  out-006.png  out-008.png
out-001.jpg  out-003.png  out-005.png  out-007.png

We got several extracted image, and one of them there was an image that contained a flag



FLAG

utctf{s1mple_w1z4rding_mist4k3s}