1

I have a CTFd instance and I try to use a webshell.

For this, I have a page like this :

from flask import render_template, session, redirect, url_for

from CTFd.models import db, Users, Teams
from CTFd.utils.decorators import admins_only, is_admin

from CTFd import utils

from uuid import uuid4 #used for random hex string generation
import subprocess #for executing commands to docker


container_name = "ctfd_wettytest_1" #CHANGE ME. Use whatever name you provided to your container
#useradd_cmd = F"docker exec -it {container_name} /usr/sbin/useradd -m -s /bin/bash -p $(openssl passwd -1 {password}) {username}"
return_info = "Pour acceder a votre instance Kali, cliquez <a href=http://192.168.192.134:8080/>ICI</a><br>Username: %s<br>Password: %s" #CHANGE ME. The URL needs to be yours.

login_info = {} #this stores login info. If CTFd webapp is restarted, the information is lost. New

def load(app):
    @app.route('/docker', methods=['GET'])
    def view_docker():
        #if utils.authed(): #make sure the user is logged in
            player = Users.query.filter_by(id=session['id']).first() #get user/team id.
            if player.id  in login_info: #if there is already login info for the user
                return_data =  return_info%(login_info[player.id][0],login_info[player.id][1])
                return render_template('page.html', content=return_data) #provide the login info to the user
            else: #if there is no login info for the user
                randomvalue = uuid4().hex[0:16] #generate random hex string
                username = randomvalue[:8]
                password = randomvalue[8:]
                login_info[player.id] = [username,password] #add login info into our dictionary
                useradd_cmd = F"/usr/bin/docker exec -it {container_name} /usr/sbin/useradd -m -s /bin/bash -p $(openssl passwd -1 {password}) {username}"
                subprocess.call(useradd_cmd, shell=True) #run a command to add a user to the container
                return_data = return_info%(login_info[player.id][0],login_info[player.id][1])
                return render_template('page.html', content=return_data) #provide the login info to the user
        #else: #if user isn't logged in, send them to the login page
            #return redirect(url_for('auth.login'))

When I launch it via flask run or gunicorn with this command :

gunicorn --bind unix:app.sock --keep-alive 2 --chdir /CTFd/ --workers 3 --worker-class gevent 'CTFd:create_app()' --access-logfile '/CTFd/CTFd/logs/access.log' --error-logfile '/CTFd/CTFd/logs/error.log' --bind 127.0.0.1:8000

No problem, account is created as expected in the docker's container and the webshell can be used.

BUT, I want this app to start automatically, when I try to launch it via systemd, everithing is working expected this page, the subprocess doesn't work and acount is not created

Make this systemd service file :

[Unit]
Description=Gunicorn instance to serve ctfd
After=network.target

[Service]
User=ctfd
Group=docker
WorkingDirectory=/CTFd
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin"
ExecStart=gunicorn --bind unix:app.sock --keep-alive 2 --workers 3 --worker-class gevent 'CTFd:create_app()' --access-logfile '/CTFd/CTFd/logs/access.log' --error-logfile '/CTFd/CTFd/logs/error.log' --bind 127.0.0.1:8000


[Install]
WantedBy=multi-user.target

Not working

So, I've tried to use crontab of ctfd user (those who own the directory and member of docker's group) :

@reboot PATH=/usr/local/sbin:/usr/local/bin:/sur/sbin:/usr/bin gunicorn --bind unix:app.sock --chdir /CTFd --keep-alive 2 --workers 3 --worker-class gevent 'CTFd:create_app()' --access-logfile '/CTFd/CTFd/logs/access.log' --error-logfile '/CTFd/CTFd/logs/error.log' --bind 127.0.0.1:8000

No message in error.log

EDIT : @erik258 put me on the right way. After is comment, I started to read EVERY logs. And found it... And it's so... stupid!

It's not a gunicorn error but a docker error :

"The input device is not a TTY"

So, using '-i" instead of "-it" because there is not any TTY when launched via systemd solve the problem.

  • "Not working" but why? you need to check the systemd logs to see why. Cron is a poor substitute for systemd, but that also has logs. clearly your app isn't starting properly, so *its* logs are not yet relevant. – erik258 Jul 07 '23 at 15:30
  • 1
    Thank you, your comment forced me to read line by line the journalctl logs. And I finally found (I've edited question). So thank you – Kptainflintt Jul 07 '23 at 16:13
  • Does this answer your question? [Error "The input device is not a TTY"](https://stackoverflow.com/questions/43099116/error-the-input-device-is-not-a-tty) – erik258 Jul 07 '23 at 16:46

0 Answers0