Home NahamCon EU CTF 2022 | Writeups
Post
Cancel

NahamCon EU CTF 2022 | Writeups


NahamCon EU CTF 2022 was a 24-Hour CTF contest hosted by JustHacking on December 16th, 12:00 PM - December 17th, 12:00 PM PST. Our team T34M #4294967295, consisting of me, Anorak and Jokesta, participated and secured 27th place in the CTF. Here are the writeups for some of the flags we captured.


Math Smashers - Scripting

Description:

So this is supposed to progressively get “harder” and has a time cap per equation. Give your answer to the nearest 4 decimal places unless there’s a trailing 0.

Upon visiting the given link, we come up with a page with an arithmetic question in an image which we need to solve and submit within a decreasing time limit. Each correct answer will increase your score by 1, and any wrong answer resets it back to 0.

Approach

We used Pytesseract to generate strings from images and used eval to solve the equations. We had to grayscale the image and whitelist characters in an arithmetic equation for better accuracy.

Final Script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import pytesseract
import requests
import cv2
import numpy as np
from bs4 import BeautifulSoup
import sys

BASE_URL = "http://challenge.nahamcon.com:"+ str(sys.argv[1]) +"/"
flag_found=False
while not flag_found:
    img_response = requests.get(BASE_URL + "/static/eqn.png")
    image = cv2.imdecode(np.frombuffer(img_response.content, np.uint8), cv2.IMREAD_GRAYSCALE)
    config = '--oem 0 --psm 6 -c tessedit_char_whitelist=0123456789+-*/^()'

    arithmetic_problem = pytesseract.image_to_string(image, lang='eng', config=config)
    result =0
    try:
        result = eval(arithmetic_problem.replace(" ", ""))
    except:
        print(arithmetic_problem)
        break
    result = round(result, 4)
    data = {"eqn_ans": result}
    response = requests.post(BASE_URL, data=data)
    soup = BeautifulSoup(response.text, 'html.parser')
    count_element = soup.find('p', class_='count')
    print(count_element.text + ": " +  str(result))


The Space between Us - Miscellaneous:

Description:

I’ve never felt this close to a character before. I hope the feeling is mutual…

Escalate your privileges and retrieve the flag from the root’s home directory.

Approach:

We were given an NC command to connect to a machine, There were only limited tools installed into the system, And for the Hell, even cd wasn’t present. We could not use the space character directly as it was getting filtered out. We had to use ${IFS} instead, an env variable for space. Though this worked in most cases, it was terrible. Anorak switched to a python reverse shell as it was installed in the system, making it easier to work around. We discovered that the `/etc/password is writable by the user, so we added a new root-privileged user and gained root.


Rick - Reverse Engineering:

Description:

These files were on a USB I bought from a pawn shop.

Approach.

We were given 2 files, ct.enc and a binary file called a program. ct.enc consisted of encrypted text. Upon analyzing with ghidra, we found that it performed AES 128 encryption on the input. The key for it was generated during the execution. We reused the reversed code from the binary to regenerate the key and decrypt our message.

Script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <iostream>
using namespace std;
unsigned char* enc_key =new unsigned char[16];
#include <openssl/evp.h>
#include <cstring>

int dec_func(unsigned char *ciphertext, int ciphertext_len, unsigned char *key, unsigned char *iv) {
  EVP_CIPHER_CTX *ctx;
  int len;
  int plaintext_len;
  unsigned char *plaintext;

  // Allocate and initialize a new cipher context
  ctx = EVP_CIPHER_CTX_new();

  // Initialize the context for a decryption operation using the
  // AES cipher in CBC mode with the given key and IV
  EVP_DecryptInit(ctx, EVP_aes_128_cbc(), key, iv);

  // Allocate a buffer to hold the plaintext
  plaintext = (unsigned char*)malloc(ciphertext_len);

  // Decrypt the ciphertext and store the resulting plaintext in the buffer
  EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len);
  plaintext_len = len;

  // Finalize the decryption operation
  EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
  plaintext_len += len;

  // Clean up the cipher context
  EVP_CIPHER_CTX_free(ctx);
  cout << plaintext;
  return plaintext_len;
}

void gen_key(void){
  unsigned int local_10;
  int local_c;
  
  for (local_c = 0; local_c < 16; local_c = local_c + 1) {
    if (local_c == 0) {
      local_10 = 0x27e2;
    }
    else {
      local_10 = (local_10 + local_c) * 4 ^ 0x29fa;
    }
    (enc_key)[local_c] = (char)local_10;
  }
  return;
}

int main() {
    FILE *f = fopen("ct.enc", "rb");
    fseek(f, 0, SEEK_END);
    long ciphertext_len = ftell(f);
    rewind(f);
    unsigned char *ciphertext = (unsigned char*)malloc(ciphertext_len);
    fread(ciphertext, 1, ciphertext_len, f);
    fclose(f);
    gen_key();
    unsigned char* enc_int= new unsigned char[16];
    memset(enc_int,0,16);
    dec_func(ciphertext, ciphertext_len, enc_key, enc_int);
}

Byepass - Web

Description:

Help yourself Say Goodbye to days gone by with our easy online service! Upload your photos to capture the memory, cherish them with friends and family, and savor the time we have together!

Approach:

We were given source code, and URL to a PHP(Apache) based web server. It had a file upload functionality and blacklisted several PHP files but not .htaccess, so we used htshells to generate a web shell, uploaded it and obtained the flag.


ShapeShifter

Description:

These bits are shapepshifting! I need some help getting them back to their original form. Someone told me this might be a Fibonacci LFSR.

Approach:

There was an output.txt with encrypted text and the python encryption program. It encrypted 2 bytes at one time. All we had to do was just script to brute force the LFSR encryption and, done, get the flag.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
from itertools import permutations, product
from Crypto.Util.number import bytes_to_long as b2l

FLAG = open('flag.txt', 'r').read().strip().encode()


class LFSR():
    def __init__(self, iv):
        self.state = [int(c) for c in iv]
        # self.state = self.iv

    def shift(self):
        s = self.state
        newbit = s[15] ^ s[13] ^ s[12] ^ s[10]  # ^ s[0]
        s.pop()
        self.state = [newbit] + s


if __name__ == "__main__":
    arr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c',
           'd', 'e', 'f', 'l', 'g', '{', '}']
    r = 2
    chars = [f'1000110001010111', '1111001000000010','1110110010100010','0111111111011101','1000010111110110','0011111000100000','0100001000010110','0001111000001010','0111000100101010','0101100111100011',
             '0000000101011010','1010111100001010','0010101111110000','1110100111110111','1001101000100010','0010001111000001','0111101111111100','0011110000010101','0110110010001100'
             ]
    flagdec = ''
    mychars = [p for p in product(arr, repeat=2)]
    for i, char in enumerate(chars):
        for flag in mychars:
            strg = ''.join(flag)
            curr = bytes(strg, 'utf-8')
            chars = f'{b2l(curr):016b}'
            assert len(chars) == 16
            lfsr = LFSR(chars)
            for _ in range(31337):
                lfsr.shift()
            finalstate = ''.join([str(c) for c in lfsr.state])
            if (f'{finalstate}' == f'{char}'):
                flagdec = flagdec + strg
                break
    print(flagdec)


This was it.

:P I will see If I can add writeups for IP Man, RaaS and Dizzy, which were solved by Anorak. The rest we solved were pretty easy and self-explanatory.

Meanwhile, Here’s an eye hurting certificate from the event:

This post is licensed under CC BY 4.0 by the author.