X-MAS CTF 2020

Capture The Flag (CTF) Competition organize by HTsP

CRYPTOGRAPHY

Scrambled Carol

We got two files which are output.txt and the python script

The output looks like this

3b180e0b05d71802070d0ed31918.....

I tried to play with the script and input only a and got this as output

After the event I tried to read @Hong5489 writeup which he is a very good CTF player >.<. I tried to bruteforce random character mapping and found out one that looks like a sentence but with random letters.

It starts with spaces so how about we try create a list with spaces in front and shuffle after that.

import string,random
from collections import Counter, OrderedDict

#Read output
output = open("output.txt","r").read().decode('hex')

#Get ASCii Letters
temp_alpha = list(string.ascii_uppercase)

#Guessing Time
while True:
    random.shuffle(temp_alpha)
    alpha = [" "] + temp_alpha
    
    #Get Distinct Letters & Frequency
    freq = Counter(output)

    #Sorts and get the frequency
    sorts = sorted(freq.items(), key=lambda i: i[1], reverse=True)

    #Mapping each with alphabet
    mapping = {}
    for i,text in enumerate(sorts):
        if i < 27:
            mapping[text[0]] = alpha[i]

    #If in mapping get the text
    flag = ""
    for t in output:
        if t in mapping.keys():
            flag += mapping[t]
    #If "flag" found in text print
    if ("flag" in flag.lower()):
        print("Alphabets : ",alpha)
        print(flag.lower())

Get a few results and tried with quipquip and manage to get the flag.

FLAG => xmaswasneverasgoodasitisthisyear

FORENSIC

Conversation

We are given one logs.pcapng file and when I encounter with pcap I will try to strings with a larger length specify and try looks for the flag format or = symbol which could be it encoded with BASE64 .

strings logs.pcapng -n  50 | grep =

This give us one result like this

Let's decode the BASE64 and we got the flag!

MISC

Complaint

This challenge will accept an input and it will redirect to /dev/null. We can imagine it looks like this

INPUT > /dev/nulll

So one idea i got is to append bash before the input so that we can get shell first instead of redirect to /dev/null . Thus we will get a shell using commands below.

bash;bash;

And once we get the shell just use base64 to encode and decode the flag.txt because there is no strings or cat . We got the flag!

Whisper's of Ascalon

Thanks to one of my teammate @AsylumxUhsylum We are given one image.png

He manage to reverse search this image and found out that this is from Guild Wars 2 . He found this!

Thus we can decode it and will get the flag!

X-MAS{GW2MYFAVORITEGAME}

PROGRAMMING

Biggest Lowest

This challenge credits to my teammate @Kaitorque who manage to solve this. This one is a simple challenge where we will get 50 test number and it will give us k1 and k2 which we need to sort the array and input ascending;descending with the amount specify in k1 and k2.

Here is the full flag.py

from pwn import *
import re

r = remote("challs.xmas.htsp.ro", 6051)

for x in range(50):
    t = r.recvuntil("array = ")
    t = r.recvline().decode('UTF-8').rstrip()
    print(t)
    t = re.sub(r'[^\w\s]', '', t)
    arr = list(map(int, t.split(' ')))
    print("Array: ", arr)
    t = r.recvuntil("k1 = ")
    k1 = int(r.recvline().decode('UTF-8').rstrip())
    print("K1: ", k1)
    t = r.recvuntil("k2 = ")
    k2 = int(r.recvline().decode('UTF-8').rstrip())
    print("K2: ", k2)
    arr.sort()
    print("Sorted Array: ", arr)
    inp = ', '.join(map(str, arr[:k1]))+"; "+', '.join(map(str, arr[:-k2-1:-1]))
    print(inp)
    r.sendline(inp)
t = r.recvuntil("}").decode('UTF-8').rstrip()
print(t)

Thus we will get the flag!

Least Greatest

This one me and @Kaitorque struggle on getting the code run quickly. We have try using a lot of ways but the finally manage to make it fast. Here is reference for this challenge

Reminder if you encounter with some sort of problem when do division just try

Try  a//b if a/b is not working

Here is flag.py

from pwn import *

def totalPrimeFactors(n):  
    # To keep track of count  
    count = 0;  
  
    # 2s that divide n  
    if ((n % 2) == 0):  
        count += 1;  
        while ((n % 2) == 0):  
            n //= 2;  
  
    # n must be odd at this point.  
    # So we can skip one element  
    # (Note i = i +2) 
    i = 3; 
    while (i * i <= n):  
          
        # i divides n  
        if ((n % i) == 0):  
            count += 1;  
            while ((n % i) == 0):  
                n //= i;  
        i += 2; 
  
    # This condition is to handle the  
    # case when n is a prime number  
    # greater than 2  
    if (n > 2):  
        count += 1;  
    return count;

def countPairs(G, L):  
    if (L % G != 0):  
        return 0
    div = int(L // G)
    print("Division: ", div)
    return (1 << totalPrimeFactors(div))


def main():
    for x in range(100):
        print("Question: ", (x+1))
        t = r.recvuntil("gcd(x, y) = ")
        gcd_n = int(r.recvline().decode('UTF-8').rstrip())
        print("GCD: ", gcd_n)
        t = r.recvuntil("lcm(x, y) = ")
        lcm_n= int(r.recvline().decode('UTF-8').rstrip())
        print("LCM: ", lcm_n)
        inp = countPairs(gcd_n, lcm_n)
        r.sendline(str(inp))
        print("Pair: ", inp)
        t = r.recvuntil("!").decode('UTF-8').rstrip()
        print(t)
        if t == "That is not the correct answer!":
            return
    t = r.recvuntil("}").decode('UTF-8').rstrip()
    print(t)

r = remote("challs.xmas.htsp.ro", 6050)
t = r.recvuntil("90 seconds.")
main()

Thus we get the flag!

WEB EXPLOITATION

Santa's Consolation

We are given one code in the link they give.

Let's try run the code.

It's a Javascript Challenge and need to identify the correct parameter for win(). If we try to enter something in win("something") . We will get the output like below.

I tried to understand their code by reverse engineering and here is what I can get.

function check(s) {
    const k = "<BASE64>";
    //Base64 Decode => Reverse
    const k1 = atob(k).split('').reverse().join('');
    console.log(bobify(s));
    //Compare bobify(input) with k1
    return bobify(s) === k1;
}
function bobify(s) {
    //Cannot Have a t e i z in input
    if (~s.indexOf('a') || ~s.indexOf('t') || ~s.indexOf('e') || ~s.indexOf('i') || ~s.indexOf('z'))
        return "[REDACTED]";
    //Replace 4 with a
    //Replace 3 with e
    //Replace 1 with i
    //Replace 7 with t
    //Replace _ with z
    const s1 = s.replace(/4/g, 'a').replace(/3/g, 'e').replace(/1/g, 'i').replace(/7/g, 't').replace(/_/g, 'z').split('').join('[]');
    //Input wil be join with [] => b[]j[]l[]l[]
    const s2 = encodeURI(s1).split('').map(c=>c.charCodeAt(0)).join('|');
    //Convert to ascii(a)+ascii([)+ascii(])+ascii(c)
    const s3 = btoa("D@\xc0\t1\x03\xd3M4" + s2);
    return s3;
}
function win(x) {
    //Check if true return flag else REDACTED
    return check(x) ? "X-MAS{" + x + "}" : "[REDACTED]";
}

To get the flag we can try reverse the variable k1 that we had.

FROM BASE64 > REVERSE > FROM BASE64 => btoa("D@\xc0\t1\x03\xd3M4" + s2)

Remove that part btoa("D@\xc0\t1\x03\xd3M4")and decode it like below

1. REPLACE "|37|53|" = " " 
2. REPLACE "|" = " "
3. REPLACE "BD" = ""
4. REPLACE a = 4
5. REPLACE e = 3
6. REPLACE i = 1
7. REPLACE t = 7
8. REPLACE z = _

Thus we will get the flag!

Last updated