level19
level19
Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: anticheat_timing_bypass, line_by_line_request, raw_socket_http, url_encoding_special_chars
Summary
Walkthrough of the CUSTOM Web level19. Task: Web challenge with anticheat proxy blocking automated tools. Solution: Send POST request with URL-encoded ASCII art via raw sockets with 1-second delays between lines to bypass timing-based bot
Description
Web challenge with an anticheat proxy requiring a POST request with URL-encoded ASCII art via raw sockets with delays between lines.
Analysis
Initial Reconnaissance
- URL
http://kslweb1.spb.ctf.su/second/level19returns 403 Forbidden with redirect to port 58080 - Server: nginx/1.18.0 (Ubuntu) with PHP/5.5.9-1ubuntu4.21
Anticheat Proxy
- Port 58080 contains an anticheat proxy that detects automated tools (curl, requests)
- The proxy checks whether HTTP request lines are sent all at once (tool) or one by one (human)
- Solution: use raw sockets with
time.sleep(1)between each line
Task
GET request returns instructions:
Nope, you should send one POST field 'smiley' with this exact face - it contains 6 lines of text:
+|||||+
| - - |
| O_o |
| | |
| \__ |
\___/
(how post values are encoded? if google doesnt help maybe we can see how it is transmitted on some other website?)
Solution
URL-encoding ASCII Art
The 6-line ASCII smiley needs to be encoded for application/x-www-form-urlencoded:
- Newlines
\n→%0A - Pipes
|→%7C - Plus
+→%2B - Backslash
\→%5C - Forward slash
/→%2F - Spaces →
+
Sending via Raw Sockets
python read-only • copy allowed#!/usr/bin/env python3 import socket import time import urllib.parse smiley = '+|||||+\n| - - |\n| O_o |\n| | |\n| \\__ |\n \\___/' encoded = urllib.parse.urlencode({'smiley': smiley}) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('kslweb1.spb.ctf.su', 58080)) s.settimeout(10) lines = [ 'POST /second/level19/ HTTP/1.1\r\n', 'Host: kslweb1.spb.ctf.su\r\n', 'Content-Type: application/x-www-form-urlencoded\r\n', f'Content-Length: {len(encoded)}\r\n', 'Connection: close\r\n', '\r\n', encoded ] for line in lines: s.send(line.encode()) time.sleep(1) response = b'' while True: try: chunk = s.recv(4096) if not chunk: break response += chunk except: break s.close() print(response.decode())
Response: Done! Flag: KSL{REDACTED}
Key Takeaways / Lessons
- Mastering anticheat_timing_bypass, line_by_line_request, raw_socket_http, url_encoding_special_chars was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-03-08 • CUSTOM • level19 • Difficulty: Medium