Skip to content

Latest commit

 

History

History
282 lines (224 loc) · 10.4 KB

File metadata and controls

282 lines (224 loc) · 10.4 KB

Purofle's Writeup

先自我介绍:
Github: Purofle
在校高三生,实在没时间做,只做了一些简单的题目

签到

看到这道题之后,直接给 JavaScript 打了个断点,然后手动把答案复制了进去,然后就过了。

喜欢做签到的 CTFer 你们好呀

打开 USTC 校内 CTF 站内招新主页之后发现好熟悉的页面,这不就是 LiveTerm(

好巧,我的主页也是基于 LiveTerm 的:archlinux.tech

使用 help 查看所有可用的指令,在乱尝试几次后,发现 env 会输出第一个 flag。

ctfer@ustc-nebula:$ ~
env
PWD=/root/Nebula-Homepage
ARCH=loong-arch
NAME=Nebula-Dedicated-High-Performance-Workstation
OS=NixOS❄️
FLAG=flag{actually_theres_another_flag_here_trY_to_f1nD_1t_y0urself___join_us_ustc_nebula}
REQUIREMENTS=1. you must come from USTC; 2. you must be interested in security!

第二个 flag 直接使用了 F12 大法,在 _next/static/chunks/pages/index-5cb01f7ec808f452.js 中搜索 flag 得到以下内容:

return t.("return", ".flag\noh-you-found-it\nAwards\nMembers\come-to-USTC-Nebula-sHomepage\nand-We-are-Waiting-or-U/");

发现 flag 在 .flag 文件中,于是直接 cat .flag 得到第二个 flag。

猫咪问答(Hackergame 十周年纪念版)

1~5 题我跟 官方 Writeup 解法相同,这里不再重复(

6.大语言模型会把输入分解为一个一个的 token 后继续计算,请问这个网页的 HTML 源代码会被 Meta 的 Llama 3 70B 模型的 tokenizer 分解为多少个 token?

这里我在 GitHub 找到了 LLaMA 3 的在线分词器:belladoreai/llama3-tokenizer-js

直接把 HTML 源代码粘贴进去,得到结果为 1833 个 token。

打不开的盒

电脑上正好有拓竹的切片软件,stl 模型下载之后直接使用 Bambu Studio 打开,点切片拖到下面,然后就看到 flag 了。 image

比大小王

打开题目后先人工尝试,发现不可解(或许机器人可以(x)),F12 后发现其实就是发请求给 /game 拿到游戏数据后通过 submit 函数提交,于是写 JavaScript 脚本模拟提交,得到 flag。

fetch('/game', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({}),
      })
        .then(response => response.json())
        .then(data => {
            answer = []
            data.values.forEach(v => {
                if (v[0] > v[1]) {
                    answer.push(">")
                } else {
                    answer.push("<")
                }
            })
            setTimeout(() => {
                submit(answer)
            }, 5000);
        })

注意 setTimeout 的时间不可少,不然会提示「检测到时空穿越,挑战失败!」。

旅行照片 4.0

LEO_CHAN?

高德地图直接搜索「科里科气科创驿站」,找到了这么一个地方叫「科里科气科创驿站」(科大站)。发现旁边就是中科大,观察地图后得出答案为东校区西门。

Note

这道题成功让我搞明白了中科大到底有几个校区(

OMINOUS_BELL

使用新版 Chrome 单独打开照片,右键选择 Sarch with Google Lens,框选左下角的车,得出答案是 CRH6F-A。 image

第二题依旧是 Search with Google Lens 的界面,找到标题为“北京有一趟仅仅4节车厢的粉色动车:周末特别火”的新闻,点进去发现这辆车是 怀密号

仔细观察图片后发现图片内并无普速列车,所以确定目标为动车运用所。

Note

此处需要车迷的直觉(x)

通过高德地图不难发现北京有三个动车运用所,分别是北京北,北京朝阳,北京南。因为怀密号始发为北京北,所以直接看北京北动车运用所的卫星照片,发现了拍摄点为积水潭医院。

PaoluGPT

下载题目源代码,查看 main.py 后,发现明显 SQL 注入点:

results = execute_query(f"select title, contents from messages where id = '{conversation_id}'")

问题是我并不会 SQL 注入(x),所以直接搜索 SQL 注入的 payload(没错我的 SQL 注入是现场在维基百科学的),找到了 1' or '1'='1,于是直接尝试,构建出以下 conversation_id:

1' or '1'='1' AND contents like '%flag%

于是获取到了 flag1,那么 flag2 怎么获取呢?这里我用了一个非常聪明的办法,flag1 开头是 flag{zU1_xiA0 那么我排除掉这些字符,剩下的 flag 就是 flag2 了(

1' or '1'='1' AND contents NOT like '%flag{zU1_xiA0%' AND contents like '%flag%

链上转账助手

转账失败

这道题的 flag1 拿的实在是意外,执行题目源码目录下的 compile.py 后,直接把 challenge1.sol 编译出的 bytecode 复制过去,于是就拿到 flag 1 了。

原理在看了官方 writeup 才明白,原来是合约默认行为是收到转账的时候会回滚

转账又失败

第二题直接 revert 不行了,于是想想别的招(

Note

我是真的抱着试一试的心态写出来的这一坨,没想到直接拿到 flag2 了

BatchTransfer 中发现了没用到的函数 withdrawPending,诶...这个函数是啥呢?要不...在 receive 里面调用一下这个函数试试?

contract Hakc {
    BatchTransfer public batchTransfer;

    receive() external payable {
        batchTransfer = BatchTransfer(msg.sender);
        batchTransfer.withdrawPending();
    }
}

然后直接编译 Hakc 合约,拿到 flag2。

不太分布式的软总线

What DBus Gonna Do?

flag1 查看源码后,发现调用 cn.edu.ustc.lug.hack.FlagServicegetFlag1 函数并且带上 string 类型的 "Please give me flag1" 作为参数即可拿到 flag1:

#!/bin/bash

dbus-send --system \
          --dest=cn.edu.ustc.lug.hack.FlagService \
          --type=method_call \
          --print-reply \
          /cn/edu/ustc/lug/hack/FlagService \
          cn.edu.ustc.lug.hack.FlagService.GetFlag1 \
          string:"Please give me flag1"

Note

开始时候忘记 --system 了,导致一直报错,后来才知道这是系统总线

If I Could Be A File Descriptor

观察 flagserver.c 发现 GetFlag2 的参数有以下要求:

  • 需要一个文件描述符
  • 需要一个 gint32 类型的参数作为 fd_index
  • fd 不能指向系统上的文件
  • fd 必须可被读取
  • fd 读取到的内容必须是 Please give me flag2\n

经过整理之后,我们可以写出以下 C 代码:

#include "gio/gunixfdlist.h"
#include "glib.h"
#include <time.h>
#include <fcntl.h>
#include <gio/gio.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>

#define DEST "cn.edu.ustc.lug.hack.FlagService"
#define OBJECT_PATH "/cn/edu/ustc/lug/hack/FlagService"
#define METHOD "GetFlag2"
#define INTERFACE "cn.edu.ustc.lug.hack.FlagService"

int main() {
  GError *error = NULL;
  GDBusConnection *connection;
  GVariant *result;

  connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
  if (!connection) {
    g_printerr("Failed to connect to the system bus: %s\n", error->message);
    g_error_free(error);
    return EXIT_FAILURE;
  }

  GUnixFDList *fd_list = g_unix_fd_list_new();
  int fd[2];
  if (pipe(fd) == -1) {
    perror("pipe");
    exit(EXIT_FAILURE);
  }

  const char *message = "Please give me flag2\n";
  write(fd[1], message, strlen(message));


  g_unix_fd_list_append(fd_list, fd[0], NULL);

  // Call the D-Bus method
  result = g_dbus_connection_call_with_unix_fd_list_sync(
      connection,
      DEST,                         // destination
      OBJECT_PATH,                  // object path
      INTERFACE,                    // interface name
      METHOD,                       // method
      g_variant_new("(h)", 0), // parameters
      NULL,                         // expected return type
      G_DBUS_CALL_FLAGS_NONE,
      -1,      // timeout (use default)
      fd_list, // Unix FD list
      NULL, NULL, &error);

  if (result) {
    gchar *flag;
    g_variant_get(result, "(s)", &flag);
    g_print("%s\n", flag);
    g_variant_unref(result);
  } else {
    g_printerr("Error calling D-Bus method %s: %s\n", METHOD, error->message);
    g_error_free(error);
  }

  g_object_unref(connection);

  return EXIT_SUCCESS;
}

这里我使用 pipe 创建了一个管道,然后将 Please give me flag2\n 写入管道,最后将管道的文件描述符传递给 D-Bus,成功拿到 flag2。

Comm Say Maybe

第三个 flag 给了 getflag3.c,但是它不会输出结果:

if (result) {
  g_print("Get result but I won't show you :)\n");
  g_variant_unref(result);
}

我们可以通过修改 getflag3.c 来输出结果:

if (result) {
    // g_print("Get result but I won't show you :)\n");
    // print result
    gchar *flag;
    g_variant_get(result, "(s)", &flag);
    g_print("%s\n", flag);
    g_variant_unref(result);
}

经过编译上传后,依旧无法获取 flag3。查看 flagserver.c 发现会使用 /proc/%d/comm 检测调用者的名字是否是 flag3,直接使用 prctl 给进程设置名字即可:

#include <sys/prctl.h>
#include <unistd.h>

prctl(PR_SET_NAME, "getflag3");

编译上传后,成功拿到 flag3。

零知识数独

数独高手

这道题的数独我是真的不会,直接搜索了数独的解法,找到了 LeetCode 37.解数独

一个个对着把题目的数独填进去,然后直接提交,拿到了 flag1。 image

总结

今年的 Hackergame 很好玩,但是因为高三时间原因,只靠周末时间研究了一小部分题目,希望明年能有更多时间参与 Hackergame!