HacktivityCon 2021

crypto

Hexahedron

Solved by: Taz34

  • We are give values of n,e and c.
  • Decode them into simple numeric form using python.

  • it looks like RSA, we used RsaCtfTool to decipher it.

: https://github.com/Ganapati/RsaCtfTool

python3 RsaCtfTool.py -n 112339816301925396926211289689793745814213925314273886071305785874178028552510482239036537066616690493241410015435402110525284201411608164205573122430898583517515498250410244592963132324072861567753086739636553410154316180827724708002409356129254383468446158145079982391991062389788544378839486986385137994309 -e 3 --uncipher 2217344750798178599616518881851238192046537371134831984828894413752520937378161486880269974456574131502921272953104454680926482208357166098075344508240480152890914678813031666242202555794691235412837030045499161787224264164243336308650477343133919653356349913604131486721125

here we have the flag.


N1TP

Solved By : choco

We are given an encrypted flag with randomized key. The hint is that this uses a one time pad encryption

The general gist of one pad encryption: You have a plain text: P = “HELLO” (8 5 12 12 15) We are given a key: K = “DREFX” (4 18 5 6 24)

The encryption comes in when we add both P and K to get encryption C = “L W Q R M” (12 23 17 18 13(39%26))

So P + K = (C%N) where N is the letter range To get the decipher text we simple subtract C with K P = “HELLO” (8 5 12 12 15(11%26)

So (P%N) = C - K

The vulnerability comes in if the same key K is used to encrypt another plain text provided we know the range of text used along with the length

Suppose P1 = “AAAAA” (1 1 1 1 1) P1 + K = C1 C1 = “ETFGY” (5 19 6 7 25)

We could easily get the key if it is within the modulus range C1 - P1 = K K = “DREFX” (4 18 5 6 24)

We can now use this to get P with the given C

But another way to find P is using simple logic

C1 - P1 = C - P So P = C - C1 + P1 All we need to do is find C - C1 to 0 and we can guess P

Using this logic we will find the flag The code for difference C - C1 is given below and difference is added to P1 and manually repeated until we get C - C1 as 0

import binascii

A = "dc0de91facbe90ee6f652167906ee0d17123cf9e746a63db4b4e7d93040f59331ead9be0b2fe"
Ahex = binascii.unhexlify(A) 
B = "dc0de91facbe90ee6f652167906ee0d17123cf9e746a63db4b4e7d93040f59331ead9be0b2fe"

Al = list(bytearray(Ahex))
Bl = list(bytearray(binascii.unhexlify(B)))
C = "flag{00000000000000000000000000000000}"

Chex = list(bytearray(C))
print(Chex)

d = []
for i in range(len(Al)):
    d.append(Al[i] - Bl[i])
print(d)

k = []
c = []
for i in range(len(Al)):
    k.append(Chex[i] + d[i])
    if((48 <= k[i] <= 57) or (97 <= k[i] <= 102) or k[i] == 108 or k[i]==103 or k[i]==123 or k[i]==125):
            c.append(chr(k[i]))
    else:
            c.append("#")
print(k)
print(c)

flag: flag{9276cdb76a3dd6b1f523209cd9c0a11b}


TRIFORCE

Solved By : choco

This is a CBC AES encryption (Chained Block Cipher)

Suppose the plain text P is 64 bits long It is then split into 4 parts (16 bit each)

There is an initializer iv that is 16 bits long and key k (16 bits long) The cipher text for first part of P is

C0 = E(P0 ^ iv,k) and following chain goes like this Ci = E(Pi ^ Ci-1,k) i > 0

Cipher block chaining (CBC) mode encryption

The decryption does like this

Cipher block chaining (CBC) mode decryption

Where P0 = D(C0,k) ^ iv and following decryption goes Pi = D(Ci,k) ^ Ci-1

If the key k and vi are same, a vulnerability arises During decryption, P0 = D(C0,k) ^ k P1 = D(C1,k) ^ C0 P2 = D(C2,k) ^ C1

if we replace C2 with C0 P2 = D(C0,k) ^ C1

xor the result with any of the two inputs will give the other input D(C0,k) = P2 ^ C1 P0 = D(C0,k) ^ k k = D(C0,k) ^ P0 therefore, k = P2 ^ C1 ^ P0 so since we know P0,P2 and C1, we can easily find k

Hence all we need to do is to replace the 3rd block of ciphertext with the first block of cipher text, Xor the result of 3rd plain text block with 2nd cipher text block and xor that result with 1st plaintext block to get the key

Do this three times with the different keys above to get the flag input: 616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161

Code for replacing ciphertext:

def listToString(s): 
    str1 = ""   
    for ele in s: 
        str1 += ele   
    return str1 

def chunk(in_string,num_chunks):
    chunk_size = len(in_string)//num_chunks
    if len(in_string) % num_chunks: chunk_size += 1
    iterator = iter(in_string)
    for _ in range(num_chunks):
        accumulator = list()
        for _ in range(chunk_size):
            try: accumulator.append(next(iterator))
            except StopIteration: break
        yield ''.join(accumulator)
        
string = "e5cc7f05f279ca152d3fbd32d899b55fec8de5d9ae3d62bac51195ddedc0863462021d3c51a4a56555ca95ff4303b764a02a2f44327cb2007d46de117900720a"
l = list(chunk(string,4))
l[2] = l[0]
print(l)
out = listToString(l)
print(out)

code for getting hex for part of flag:

def chunk(in_string,num_chunks):
    chunk_size = len(in_string)//num_chunks
    if len(in_string) % num_chunks: chunk_size += 1
    iterator = iter(in_string)
    for _ in range(num_chunks):
        accumulator = list()
        for _ in range(chunk_size):
            try: accumulator.append(next(iterator))
            except StopIteration: break
        yield ''.join(accumulator)

string = "6227616161616161616161616161616161616161616161616161616161616161e8c6e5dfb46432e2c2499084e899d462e6af4534aed0627d75f825c096970f36"
l = list(chunk(string,4))
c = "ec8de5d9ae3d62bac51195ddedc08634"

print(hex(int(l[2],16)))

d = int(l[2],16) ^ int(c,16)
print(hex(d))
e = d ^ int(l[0],16)
print(hex(e)) ![](https://i.imgur.com/TgyoAnU.jpg)

do this and replace each cipher for each key each keys in hex = 666c61677b3831396639643864383337 32316163346334343262313635396633 36646632647d20202020202020202020

flag: **flag{819f9d8d83721ac4c442b1659f36df2d}**

misc

Shelle

Solved by : nigamelastic

on starting the shell u see that u can only use the 7 commands cat, ls, pwd, whoami, ps, id, echo as mentioned in the txt file present

Dear Students, here are the questions for your next assignment, please finish them..
If you don't wanna do the assignment you can simply submit the flag which is in the /opt directory, but hah that would be impawsible

1) What is Linux?
2) What is the difference between UNIX and LINUX?
3) What is BASH?
4) What is Linux Kernel?
5) What is LILO?
6) What is a swap space?
7) What is the advantage of open source?
8 ) What are the basic components of Linux?
9) Does it help for a Linux system to have multiple desktop environments installed?
10) What is the basic difference between BASH and DOS?


Also do learn about following linux commands.
> whoami - Prints the user name associated with the current effective user ID.
> pwd - Prints the name of current working directory
> ls - List information about the FILEs (the current directory by default)
> ps - Displays information about a selection of the active processes.
> id - Print user and group information for the specified USER, or (when USER omitted) for the current user.
> echo - display a line of text (sometimes useful to print emotes)
> cat - concatenate files and print on the standard output

most of the using the research from the previous integrity challenge I tried using a hex decoding via echo -e however everything including``(backticks) and / are filtered. luckily $()` isn’t so i crafted the payload and got the flag:

cat $(echo -e "\x2f\x6f\x70\x74\x2f\x66\x6c\x61\x67\x2e\x74\x78\x74") 
flag{82ad133488ad326eaf2120e03253e5d7}

WORD CHURCH

Solved By : choco

the whole program is about solving 30 crossword puzzles with unspecified words to find. We have to use a script to find the words and a separate script to execute and get inputs of the crossword and words then output that position to the script

The program might encounter forkbombs or slow down but it works after a while

the crossword script :

import re
from itertools import islice

def find_in_list_of_list(mylist, char):
    for sub_list in mylist:
        if char in sub_list:
            return (mylist.index(sub_list), sub_list.index(char))
    raise ValueError("'{char}' is not in list".format(char = char))

def topright(chara,lis,i,j):
        st = "["
        for k in range(len(chara)):
                if( (i > 15) or (j > 15)):
                        return "nope"
                if(chara\[k]==lis[i\][j]):
                        st = st + "(" + str(j)+", "+str(i) + "), "
                        i-=1
                        j+=1
                        print(str(i) +" "+str(j))
                else:
                        return "nope"
        st =st + "]"
        return st

def right(chara,lis,i,j):
        st = "["
        for k in range(len(chara)):
                if( (i > 15) or (j > 15)):
                        return "nope"
                if(chara\[k]==lis[i\][j]):
                        st = st + "(" + str(j)+", "+str(i) + "), "
                        j+=1
                        print(str(i) +" "+str(j))
                else:
                        return "nope"
        st =st + "]"
        return st
        
def bottomright(chara,lis,i,j):
        st = "["
        for k in range(len(chara)):
                if( (i > 15) or (j > 15)):
                        return "nope"
                if(chara\[k]==lis[i\][j]):
                        st = st + "(" + str(j)+", "+str(i) + "), "
                        j+=1
                        i+=1
                        print(str(i) +" "+str(j))
                else:
                        return "nope"
        st =st + "]"
        return st
        
def top(chara,lis,i,j):
        st = "["
        for k in range(len(chara)):
                if( (i > 15) or (j > 15)):
                        return "nope"
                if(chara\[k]==lis[i\][j]):
                        st = st + "(" + str(j)+", "+str(i) + "), "
                        i-=1
                        print(str(i) +" "+str(j))
                else:
                        return "nope"
        st =st + "]"
        return st
        
def bottom(chara,lis,i,j):
        st = "["
        for k in range(len(chara)):
                if( (i > 15) or (j > 15)):
                        return "nope"
                if(chara\[k]==lis[i\][j]):
                        st = st + "(" + str(j)+", "+str(i) + "), "
                        i+=1
                        print(str(i) +" "+str(j))
                else:
                        return "nope"
        st =st + "]"
        return st


def topleft(chara,lis,i,j):
        st = "["
        for k in range(len(chara)):
                if( (i > 15) or (j > 15)):
                        return "nope"
                if(chara\[k]==lis[i\][j]):
                        st = st + "(" + str(j)+", "+str(i) + "), "
                        i-=1
                        j-=1
                        print(str(i) +" "+str(j))
                else:
                        return "nope"
        st = st + "]"
        return st


def left(chara,lis,i,j):
        st = "["
        for k in range(len(chara)):
                if((i > 15) or (j > 15)):
                        return "nope"        
                if(chara\[k]==lis[i\][j]):
                        st = st + "(" + str(j)+", "+str(i) + "), "
                        j-=1
                        print(str(i) +" "+str(j))
                else:
                        return "nope"
        st =st + "]"
        return st
        

def bottomleft(chara,lis,i,j):
        st = "["
        for k in range(len(chara)):
                if((i > 15) or (j > 15)):
                        return "nope"
                if(chara\[k]==lis[i\][j]):
                        st = st + "(" + str(j)+", "+str(i) + "), "
                        j-=1
                        i+=1
                        print(str(i) +" "+str(j))
                else:
                        return "nope"
        st =st + "]"
        return st
        
def find(chara,lis,i,j):
        st = ""
        if(chara\[1] == lis[(i-1)%15\][(j+1)%15]):
                st = topright(chara,lis,i,j)
                if(st != "nope"):
                        return st
        if(chara\[1] == lis[i\][(j+1)%15]):
                st = right(chara,lis,i,j)
                if(st != "nope"):
                        return st
        if(chara\[1] == lis[(i+1)%15\][(j+1)%15]):
                st = bottomright(chara,lis,i,j)
                if(st != "nope"):
                        return st
        if(chara\[1] == lis[(i-1)%15\][j]):
                st = top(chara,lis,i,j)
                if(st != "nope"):
                        return st
        if(chara\[1] == lis[(i+1)%15\][j]):
                st = bottom(chara,lis,i,j)
                if(st != "nope"):
                        return st
        if(chara\[1] == lis[(i-1)%15\][(j-1)%15]):
                st = topleft(chara,lis,i,j)
                if(st != "nope"):
                        return st
        if(chara\[1] == lis[i\][(j-1)%15]):
                st = left(chara,lis,i,j)
                if(st != "nope"):
                        return st
        if(chara\[1] == lis[(i+1)%15\][(j-1)%15]):
                st = bottomleft(chara,lis,i,j)
                if(st != "nope"):
                        return st
        return "nope"
                
def maind(arg0,arg1):                
        content = arg1
        #l = list(re.sub(r'[^a-zA-Z]', '', content))
        #l1 = [l[x:x+16] for x in range(0, len(l), 16)]
        l1 = arg1
        strr = arg0
        for i in range(len(l1)):
                for j in range(len(l1[i])):
                        if(strr\[0]==l1[i\][j]):
                                print("found in " +str(i) +" "+str(j))
                                ans = find(strr,l1,i,j)  
                                if(ans!="nope"):
                                        ans1 = ans[0:(len(ans)-3)]+"]"
                                        return ans1
        return " "
if __name__ == "__main__":
        maind(sys.argv[1], sys.argv[2], sys.argv[3])

The execution script:

import crossword

from pwn import *

host = 'challenge.ctf.games'
port = 30567
counter = 0
r = remote(host, port)

if __name__ == "__main__":
        data = r.recvuntil('> ')
        r.send(b'play')
        data = r.recvuntil('> ')
        wor = data.decode('ascii')
        l3 = []
        print(wor[20])
        while True:
                try:
                        if wor != None and '0' in wor:
                                s2 = "0  |"
                                l = list(re.sub(r'[^a-zA-Z]', '', wor[wor.index(s2) + len(s2):]))
                                l2 = [l[x:x+16] for x in range(0, len(l), 16)]
                                l3 = l2[:len(l2)-1]
                                print(l3)
                                str1 = ""
                                for ele in l2\[len(l2)-1\][1:]: 
                                        str1 += ele
                                print(str1)
                                ans = crossword.maind(str1,l3)
                                print(ans)
                                r.sendline(ans)
                                data = r.recvuntil('> ')
                                wor = data.decode('ascii')
                                print(wor)
                        else:
                                stre = re.sub(r'[^a-zA-Z]', '', wor)
                                print(stre)
                                ans = crossword.maind(stre,l3)
                                print(ans)
                                r.sendline(ans)
                                data = r.recvuntil('> ')
                                print(data)
                                wor = data.decode('ascii')
                                print(wor)
                
                except EOFError:
                        print(r.recv(4096).decode())
                        exit()

flag{ac670e1f34da9eb748b3f241eb03f51b}


Bad Words

Solved By : bobbysox

This challenge was interesting. 99% of characters that I entered were flagged as “Bad Words” After some trial and error, i got the first message down below “bash: fg: no job control”

I tried to implement some job control with no luck. However, when trying this, I noticed it processed “#! /bin/bash” without throwing errors. It processes the “/” character!!!! This made me take a step back and think about what were in. We’re in a custom restricted shell. In the past ive usually used native binaries to escape such situations. We have two options here: prefix our commands with “/”, or, try and call native binaries since we know we can just call any path. The latter seemed like the best solution. Call /bin/bash and see what happens. It worked!!! yay!

mobile

To do

Solved by: Starry-lord

De-compile the app

I personally used https://www.decompiler.com/jar/62e31f7faaf148b1b1c4fd143e5480c1/todo.apk/sources/com/congon4tor/todo/LoginActivity.java

Find password “testtest”

Login to the app and find to do list along with the flag


Reactor

Solved by: Starry-Lord

Flag gets more and more unscrambled with correct digits. Basically 4 digit Pin probabilities plus dynamic deobfuscating made the total of possibilities go down to less than 40, like an Eval situation, where you would have result if your first characters are correct.

A. Input 1 digit, 0 to 5, (5) B. Input second digit 0-9(9) C. Input third digit 0-2(2) D. Input fourth digit 0-7(flag!)

27 tries on /40

I agree it’s most likely not the intended way but 4 digits pin plus Eval like function is vulnerable enough 😉

5 was the only one starting with letter f, four characters and a promising { like in other flags.

osint

Challenge Jed Sheeran

Solved by: Starry-lord

Google jed sheeran music and find a soundcloud account

https://m.soundcloud.com/user-836083929-176777888/beautiful-people

pwn

Butter Overflow

Solved by: Taz34

  • As the name suggested it’s a buffer overflow challenge
  • So I started by giving huge inputs
  • And further narrowed it down and found the offset
  • The offset is 520 so we need 521 characters to do a buffer overflow to read the flag

scripting

UHAHA

Solved By : ava and thewhiteh4t

We are given a file called uhaha, upon checking the file using the file command, we come to know that it is type of archive called UHARC, fairly rare one that is. Obvious conclusion seems like extracting it but oh, look it is password protected, and the description of challenge did mention we might need to use rockyou, so it looks like it is time for bruteforcing. Looks like we are going to need a terminal tool and write a script for bruteforcing, apparently we couldn’t find a linux tool to extract UHARC archive, running it under wine was a possibility but that would just complicate the whole thing, so we got a windows tool for it linked here - https://sam.gleske.net/uharc/ just installing it and locating uharc.exe will do the job and it looks like it is archive inside a archive….archiveception! So the following script extracts it repeatedly until we get flag.

from os import rename, remove
import subprocess as subp
filename = 'uhaha.uha'
wordlist_path = 'rockyou.txt'
def extract(filename):
    counter = 0
    with open(wordlist_path, 'r') as wordlist:
        for line in wordlist:
            counter += 1
            word = line.strip()
            #print(f'Trying {word}')
            proc = subp.Popen(['uharc.exe', 'e' , f'-pw{word}', filename], stdout=subp.PIPE)
            subp.call(['taskkill', '/F', '/PID', str(proc.pid)], stdout=subp.PIPE, stderr=subp.PIPE)
            stdout = proc.stdout.read()
            output = stdout.decode()
            if 'ERROR' in output:
                pass
            elif 'successfully' in output:
                print(f'PASSWORD : {word}')
                break
            else:
                print('Unknown error occured, weird tool...')
                print(output)
                break
            if counter > 200:
                break

extract(filename)
while True:
    try:
        rename('uhaha', 'uhaha2.uha')
    except FileNotFoundError:
        print('Extracted -> flag.png')
        exit()
    extract('uhaha2.uha')
    remove('uhaha2.uha')

warmups

Target Practice

Solved By : ava

We are given a GIF file, which actually works, and after opening we can see it has some sort of code on it, which changes pretty quick, so we need to split GIF into frames, I used https://ezgif.com/split to split and then downloaded all frames, and did a quick google re-image search on one of them, which result us in knowing that it is called MAXICODE, used by UPS. then I found i MAXICODE decoder - https://products.aspose.app/barcode/recognize/maxicode# and i had to manually check every image, which seems like not the intended way, but okie. The 15th frame (if you started counting from 0) has the flag

flag{385e3ae5d7b2ca2510be8ef4}


2EZ

Solved By : thewhiteh4t

  • We are given a file named 2ez
  • the file format is not known when I tested it with file command

  • next I tried binwalk to look for any hidden files but the output was blank
  • next I checked the MAGIC of the file i.e. the header
  • file magic is responsible for the correct file format
  • file command checks magic and file footer to determine correct file type

  • JFIF header means a jpeg file
  • correct header for JFIF in hex is : FF D8 FF
  • but if we look at the file given to us its different, so I fixed it using a hex editor

  • Saved it as a new file and solved


TSUNAMI

Solved by: Taz34

  • We are given a .wav audio file
  • at the end of the audio we can here sum disturbances
  • so i checked the audio file with sonic visualizer
  • checked with spectrograms

here is the flag.


Six Four Over Two

Solved by: Taz34

we have a cipher text give, run it through cyber chef and decode it to get the flag.


Pimple

Solved By : thewhiteh4t

  • We are given a gimp project file in this challenge
  • there are multiple layers
  • each layer contains an image
  • to see the flag I started hiding the layers one by one from top and eventually saw the flag


Bass64

Solved By : thewhiteh4t

  • We are given a text file in this challenge
  • file contains letters and numbers in ASCII art
  • it’s actually a base64 string
  • converting it gives the flag

flag{35a5d13da6a2afa0c62bfcbdd6301a0a}

ODDBALL

Solved By : choco

the given file is an octal dump

0000000 067531 020165 067165 067543 062566 062562 020144 064164
0000020 020145 062563 071143 072145 066440 071545 060563 062547
0000040 035440 005051 072516 061155 071145 020163 067151 071040
0000060 067141 062547 030040 033455 020077 066510 066555 020041
0000100 067510 020167 062157 005041 063012 060554 075547 060462
0000120 031065 061462 033062 034143 060467 031546 034461 061062
0000140 031064 031543 063064 031544 033062 034063 061065 005175
0000160

All we need to do is to convert this octal to hex dump that is split into 16 parts in each line

def octtodec(decnum):
    i = 0
    octdecnum = 0
    while decnum != 0:
        rem = decnum % 10
        octdecnum += rem * 8**i 
        decnum = decnum // 10
        i += 1
    return octdecnum

my_file = open("oddball", "r")
content = my_file.read()
content = content.replace('\n',' ')
content_list = content.split(" ")
print(content_list)
num = []
res = ""
j = 0
for i in range(len(content_list)-1):
        if(i%9!=0):
                res1 = hex(octtodec(int(content_list[i])))
                if(len(res1)<6):
                        res1 = res1[0:2]+'0'+ res1[2:]
                print(res1)
                res = res + res1[4:6] + " " + res1[2:4] + " "
                print(res)
        else:
                num = str(j*10)
                j += 1        
                res = res + "\n" + num.zfill(8) + " "
print(num)
#x = 9
#res = [content_list[i:i+x] for i in range(0, len(content_list), x)]
#print(res)

output will be this

00000000 59 6f 75 20 75 6e 63 6f 76 65 72 65 64 20 74 68 
00000010 65 20 73 65 63 72 65 74 20 6d 65 73 73 61 67 65 
00000020 20 3b 29 0a 4e 75 6d 62 65 72 73 20 69 6e 20 72 
00000030 61 6e 67 65 20 30 2d 37 3f 20 48 6d 6d 6d 21 20 
00000040 48 6f 77 20 6f 64 21 0a 0a 66 6c 61 67 7b 32 61 
00000050 35 32 32 63 32 36 63 38 37 61 66 33 31 39 32 62 
00000060 34 32 63 33 34 66 64 33 32 36 33 38 35 62 7d 0a 

you’ll get the flag when converting this hexdump

You uncovered the secret message ;)
Numbers in range 0-7? Hmmm! How od!

flag{2a522c26c87af3192b42c34fd326385b}

web

Titanic

Solved By : thewhiteh4t

  • In this challenge we were given a website of a company
  • Two things which instantly caught attention were URL Capture and Admin buttons
  • URL capture service accepts a URL and takes screenshot of the webpage

  • Admin page got a login
  • First idea was to try http://localhost and it worked

  • This is same as the loading splash screen I saw while loading the challenge website
  • Next I checked robots.txt and got 200 and this revealed a new path /server-status

  • Next I obviously tried to access /server-status and got 200 again

  • And in the logs you can see the login credentials!


SWAGGY

Solved by: Taz34

  • Change the server to the testing server

  • now authorize using the admin:admin credentials

  • now try and execute the request to get the flag

here we have the flag.


Confidentiality

Solved by: Taz34

  • here we have a service which lists all the items in the mentioned dir
  • so i simply started looking for flag, look for elements in the /home dir
  • here we have a user dir and in that we have the flag.txt

now to red the file

/home/user & cat /home/user/flag.txt ![](https://i.imgur.com/G0AlZ6s.png)

here we have the flag