{"id":14637807,"url":"https://github.com/Tp0t-Team/Tp0tOJ","last_synced_at":"2025-09-07T06:30:58.567Z","repository":{"id":38356954,"uuid":"205882456","full_name":"Tp0t-Team/Tp0tOJ","owner":"Tp0t-Team","description":"A CTF online judge platform developed by Tp0t.","archived":false,"fork":false,"pushed_at":"2024-07-20T13:39:10.000Z","size":1904,"stargazers_count":52,"open_issues_count":12,"forks_count":8,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-01-02T10:37:44.741Z","etag":null,"topics":["capture-the-flag","ctf","ctf-events","ctf-platform","ctfd","k8s","kubernetes"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Tp0t-Team.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-09-02T15:10:05.000Z","updated_at":"2024-11-22T06:06:27.000Z","dependencies_parsed_at":"2025-01-02T16:30:15.639Z","dependency_job_id":null,"html_url":"https://github.com/Tp0t-Team/Tp0tOJ","commit_stats":null,"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/Tp0t-Team/Tp0tOJ","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Tp0t-Team%2FTp0tOJ","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Tp0t-Team%2FTp0tOJ/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Tp0t-Team%2FTp0tOJ/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Tp0t-Team%2FTp0tOJ/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Tp0t-Team","download_url":"https://codeload.github.com/Tp0t-Team/Tp0tOJ/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Tp0t-Team%2FTp0tOJ/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274005341,"owners_count":25205934,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-07T02:00:09.463Z","response_time":67,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["capture-the-flag","ctf","ctf-events","ctf-platform","ctfd","k8s","kubernetes"],"created_at":"2024-09-10T02:01:16.553Z","updated_at":"2025-09-07T06:30:57.576Z","avatar_url":"https://github.com/Tp0t-Team.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"# Tp0t OJ\n\n**demo站服务器到期暂时下线,正在寻找新服务器**\n\n[![Tp0tOJ](https://github.com/Tp0t-Team/Tp0tOJ/actions/workflows/oj-build.yaml/badge.svg?branch=master)](https://github.com/Tp0t-Team/Tp0tOJ/actions/workflows/oj-build.yaml)\n\n![Alt](https://repobeats.axiom.co/api/embed/08537827caf3f05d3823e8e8b3619c97229a8a4f.svg \"Repobeats analytics image\")\n\n延续老版本的Tp0t OJ，前端继承并优化了视觉效果，后端从JAVA切换为Golang，定位也从一个面向校内的持续性训练平台，转向于一个比CTFd更好用的CTF比赛平台\n\n欢迎非盈利性质的比赛使用我们的平台，比如各大学校的校赛等，也欢迎大家提PR和ISSUE\n\n**特别提醒盈利性质的赛事服务提供商（通过提供CTF比赛服务来获取酬金）如果使用本平台提供服务同样需要遵循AGPL开源**\n\n### 我们的特性\n\n| 特性  | Tp0t OJ | CTFd | dasctf |\n| --- | --- | --- | --- |\n| K8s集群 -\u003e 一键部署题目环境 | :heavy_check_mark: | :x: | :heavy_check_mark: |\n| 题目定义与实例分离 -\u003e 原生支持动态Flag | :heavy_check_mark: | :x: | :heavy_check_mark: |\n| 预构建单执行文件 -\u003e 无其他依赖，平台一键启动 | :heavy_check_mark: | :x: | :x: |\n| 提供WriteUp的上传和下载通道 | :heavy_check_mark: | :x: | :heavy_check_mark: |\n| 动态积分+一二三血 | :heavy_check_mark: | :x: | :x: |\n| 容器调度，避免算力浪费 | :heavy_check_mark: | :x: | :x: |\n| 合理的动态积分曲线 | :heavy_check_mark: | :x: | :x: |\n\n\n## 平台部署指南\n\n### 环境准备\n\n**官方推荐Ubuntu20.04 AMD64与 Ubuntu22.04 AMD64**，release中的版本支持linux/amd64，但是未在众多的linux发行版上完成测试，如果有完成其他发行版的测试，请提交issue让我们知道\n其他系统和架构请按照[编译指南](https://github.com/Tp0t-Team/Tp0tOJ/blob/master/README.md#%E5%BC%80%E5%8F%91%E6%8C%87%E5%8D%97)自行编译，目前已经不需要依赖CGO\n\n因为题目image build依赖于docker，所以需要先安装好**和本机网段无冲突的Docker**，我们在测试过程中发现部分云服务提供商默认Docker网段和本机局域网网段冲突，导致build镜像的时候无法正常访问网络\n\n[Install Docker Engine | Docker Documentation](https://docs.docker.com/engine/install/)\n\n如果需要调整网段，在`/etc/docker/daemon.json`中配置bip字段，参考如下，网段可更改\n\n```json\n{\n    \"bip\": \"192.168.100.1/24\"\n}\n```\n\n然后重启docker服务\n\n```shell\nsudo systemctl daemon-reload\nsudo systemctl restart docker\n```\n\n请确保当前系统中有`systemd`环境 [什么是systemd](https://systemd.io/)\n\n当前用户需要在 sudo 组\n\n### 部署流程\n\n**由于SQLite对于多并发读写并不友好，所以在完成优化和测试前不再推荐使用SQLite，默认数据库更换为PostgresSQL**\n\n根据系统架构在release页面获取 ojtool 二进制文件，如果所用系统架构没有对应的release，请参照contribution自行build\n\n\n\n```shell\n./ojtool prepare -MasterIP xxx.xxx.xxx.xxx --postgres\nINSTALL_K3S_MIRROR=cn ./ojtool prepare -MasterIP xxx.xxx.xxx.xxx --postgres #for CHINA\n```\n\n**中国用户请使用Mirror的参数执行**\n\n执行`ojtool prepare` 会在当前目录下生成配置文件目录`resources`，安装完成后目录应该如下所示，其中`https.crt`、`https.key` 文件不会默认生成。平台启动时会自动检测这两个文件，如果存在，就自动启用https模式，否则采用http模式\n\n`home.html`为提供了主页面定制化的功能，在前后端一体化的单文件模式下，如果平台`resources` 目录中存在`home.html` 会将其作为平台主页展示\n\n```\n.\n├── agent-install.sh        #「在从节点服务器上使用」从节点安装文件\n├── OJ                      #平台二进制文件\n├── ojtool                  #命令行工具二进制文件\n├── resources               #配置文件目录\n│   ├── ca.crt              #镜像仓库自签名根证书备份\n│   ├── config.yaml         #「平台启动前请修改」平台配置文件模板\n│   ├── docker-registry     #镜像仓库相关目录\n│   │   ├── auth            #镜像仓库授权文件目录\n│   │   │   └── htpasswd    #授权文件\n│   │   ├── certs           #镜像仓库证书目录\n│   │   │   ├── tls.crt     #镜像仓库证书\n│   │   │   └── tls.key     #镜像仓库私钥\n│   │   └── data            #镜像仓库数据存储目录\n|   ├── [postgres]          #postgres数据目录\n│   ├── [https.crt]         #「非自动生成」网站TLS证书\n│   ├── [https.key]         #「非自动生成」网站TLS私钥\n│   ├── k3s.yaml            #自动生成的k3s配置文件，不需要修改\n|   ├── [home.html]         #「非自动生成」如果存在，会加载该网页做为主页面\n|   ├── [timeline.save]     #「启动后生成」排行榜历史数据存储文件\n|   ├── [emails]            #「非自动生成」邮件模板目录\n│   │   ├── [reset.html]    #「非自动生成」密码重置邮件模板\n│   │   └── [welcome.html]  #「非自动生成」导入账户初始化邮件模板\n│   ├── tls.crt             #镜像仓库公私钥备份\n│   └── tls.key             #镜像仓库公私钥备份\n├── [writeup]               #「启动后生成」writeup存储目录\n└── start.sh                #「配置完毕后启动」启动脚本\n```\n\n然后使用 `start.sh`启动平台\n\n`agent-install.sh`是自动生成的从节点安装文件，从节点为平台集成k8s的从节点，由随平台部署的主节点进行管理，**该脚本在需要部署的从节点服务器上运行！**\n\n将该文件拷贝到从节点服务器上，执行\n\n```shell\n./agent-install.sh xxx.xxx.xxx.xxx #从节点服务器公网IP\nINSTALL_K3S_MIRROR=cn ./agent-install.sh xxx.xxx.xxx.xxx #对于中国用户\n```\n如果需要卸载k3s，参考官网\n\nTo uninstall K3s from a server node, run:\n\n```bash\n/usr/local/bin/k3s-uninstall.sh\n```\n\nTo uninstall K3s from an agent node, run:\n\n```bash\n/usr/local/bin/k3s-agent-uninstall.sh\n```\n\n#### 平台配置说明\n\n```yaml\nserver:                                 #平台服务器的配置参数\n  host: 127.0.0.1                       #设置为Host，用于重置密码和CORS等（仅hostname部分不含协议端口和路径）\n  username: Tp0t                        #默认admin用户名\n  password: password                    #默认admin账号密码\n  mail: admin@example.com               #默认admin账号邮箱\n  port: 0                               #0时自动选择80/443，非0指定端口\n  salt: \"xxxxxxxxxx\"                    #用于密码保护的salt，自动生成\n  behaviorLog: false                    #用于记录选手关键行为，默认不开启\n  debug:                                #debug相关功能，生产环境请勿开启\n    dockerOpDetail: false               #开启可以查看容器构建和下发的问题\n    noOriginCheck: false                #开启禁用orgin检查，禁用csrf检查，禁用CSP\n    dbOpDetail: false                   #开启查看所有数据库请求\n  cookieExpiresSeconds: 3600            #cookie过期秒数，0表示不会过期，-1表示在关闭浏览器时过期\nemail:                                  #邮件服务配置\n  host: smtp.example.com                #邮件服务提供商服务器\n  username: exampleUsername             #邮件发送账号\n  password: examplePassword             #邮件发送账号密码（可能为授权码）\nchallenge:                              #题目分数控制参数\n  firstBloodReward: 0.1                 #一血分数奖励比例\n  secondBloodReward: 0.08               #二血分数奖励比例\n  thirdBloodReward: 0.05                #三血分数奖励比例\n  halfLife: 20                          #题目分值减半所需解题人数\nkubernetes:                             #k8s集群配置参数\n  portAllocBegin: 30000                 #自动分配端口范围起点\n  portAllocEnd: 31000                   #自动分配端口终点\n  username: xxxxxxxx                    #「不可修改」镜像仓库用户名\n  password: xxxxxxxx                    #「不可修改」镜像仓库密码\n  registryHost: xxx.xxx.xxx.xxx:5000    #「不可修改」镜像仓库地址（与平台一致）\ndatabase:                               #数据库连接参数\n  dsn: \"...\"                            #数据库连接配置，自动生成\ntimelineFile: \"resources/timeline.save\" #「不建议修改」排行榜历史数据存储文件路径\n```\n\n邮件服务配置为必要配置，用户重置和修改密码依赖于邮件服务，**未配置邮件服务将导致用户无法修改密码**\n\n题目分数曲线公式：\n\n$$k=\\frac{1.8414\\cdot(N_{solved}-1)}{HalfLife}$$\n\n$$RealScore=\\left\\lfloor\\frac{BaseScore}{k+e^{-k}}\\right\\rfloor$$\n\n\u003c!-- ![k=\\frac{1.8414\\cdot(N_{solved}-1)}{HalfLife}](https://latex.codecogs.com/svg.image?k=\\frac{1.8414\\cdot(N_{solved}-1)}{HalfLife}) --\u003e\n\n\u003c!-- ![RealScore=\\left\\lfloor\\frac{BaseScore}{k+e^{-k}}\\right\\rfloor](https://latex.codecogs.com/svg.image?RealScore=\\left\\lfloor\\frac{BaseScore}{k+e^{-k}}\\right\\rfloor) --\u003e\n\n题目分数曲线示例：\n\n![curve](curve.svg)\n\n基础分数1000，减半人数20，不含奖励分数情况下 动态积分曲线\n\n## 平台使用指南\n\n题目flag支持\n\n- 单flag\n- 多flag\n- 正则flag\n- 动态随机flag\n\n配置参考[config_demos](https://github.com/Tp0t-Team/Tp0tOJ_demos)\n\n需要使用k8s集群的基本就是PWN题和WEB题\n\n[Pwn_demo1](https://github.com/Tp0t-Team/Tp0tOJ_demos/tree/main/pwn1)\n\n### 镜像编译\n\n在镜像编译页面上传镜像，需要上传包含Dockerfile的tar包，**注意tar包没有额外目录层级，需要直接能获取到Dockerfile**\n\n对于PWN题，推荐使用`xinetd`作为守护进程\n\n#### Dockerfile示例\n\n**新手请严格按照demo编写题目Dockerfile**\n\n\u003e - **注意对于所有需要执行的文件，附加执行权限，否则镜像会build成功，但是用户申请创建实例的时候会失败**\n\u003e   \n\u003e - 对于singleton的题目（所有选手共用一个容器实例），请严格注意权限管控\n\u003e   \n\n```dockerfile\nFROM ubuntu:20.04\nRUN sed -i \"s/http:\\/\\/archive.ubuntu.com/http:\\/\\/mirrors.ustc.edu.cn/g\" /etc/apt/sources.list\nRUN apt-get update\nRUN apt-get -y upgrade\nRUN apt-get install -y apt-utils lib32z1 xinetd\nRUN useradd -u 8888 -m pwn\nCOPY share/libunicorn.so.1 /usr/local/lib/libunicorn.so.1\nRUN chmod 755 /usr/local/lib/libunicorn.so.1\nRUN ldconfig\nCOPY share/easiestpwn /home/pwn/easiestpwn\nRUN chmod 755 /home/pwn/easiestpwn\nRUN rm /etc/xinetd.d/*\nCOPY xinetd /etc/xinetd.d/xinetd\nCOPY entrypoint.sh /home/pwn/entrypoint.sh\nENTRYPOINT [\"/home/pwn/entrypoint.sh\"]\nEXPOSE 8888\n```\n\n#### `entrypoint.sh`示例\n\n对于动态flag的题目，必须具备`entrypoint.sh`文件，因为平台会将生成的随机flag通过`entrypoint.sh`的第一个参数的形式传入\n\n```shell\n#!/bin/sh\necho $1 \u003e flag #动态FLAG的必须行，用于平台将生成的FLAG写入，也可自行调整写入位置\n/usr/sbin/xinetd -dontfork #启动守护进程\n```\n\n#### 守护进程`xinetd`示例\n\n```\nservice pwn \n{\n    disable = no\n    type        = UNLISTED\n    wait        = no\n    server      = /bin/sh\n    # replace helloworld to your program\n    server_args = -c cd${IFS}/home/pwn;exec${IFS}./easiestpwn\n    socket_type = stream\n    protocol    = tcp\n    user        = pwn \n    port        = 8888\n    # bind        = 0.0.0.0\n    # safety options\n    flags       = REUSE\n    per_source    = 10 # the maximum instances of this service per source IP address\n    rlimit_cpu    = 1 # the maximum number of CPU seconds that the service may use\n    #rlimit_as  = 1024M # the Address Space resource limit for the service\n    #access_times = 2:00-9:00 12:00-24:00\n    nice        = 18\n}\n```\n\n#### 打包示例\n\n打包可以使用gz、xz压缩，但是推荐使用tar直接打包，打包不能包含当前文件夹\n\n举个例子，如果Dockerfile等文件都放在`/home/pwn/example`下，需要运行以下指令打包\n\n```shell\ncd /home/pwn/example\ntar -cvf ../example.tar ./*\n```\n\n### 题目部署\n\nyaml文件用于上一键导入题目信息以及配置题目所属的K8S节点，需要使用节点部署的题只能通过导入config的形式添加\n\n```yaml\nname: dynamic_flag              # challenge name\ncategory: PWN                   # support [MISC|RE|PWN|WEB|CRYPTO|HARDWARE|RW]\nscore:\n  baseScore: 1000               # base score\n  dynamic: true                 # if the score change with solved number\nflag: \n  value: xxxxxxxxxx             # means that random dynamic flag length is 10\n  type: 3                       # 0-Single 1-Multiple 2-Regexp 3-Dynamic\ndescription: \"description\" \nexternalLink: [\"http://link\"]\nsingleton: false                # false means this challenge will give every ctfer a container\n\n# below is no need for singleton challenge\nnodeConfig:\n  - name: \"pwn1\"                # give this name same as your uploaded image\n    image: \"pwn1\"               # give this name same as your uploaded image\n    servicePorts:               # default \u0026 DON'T CHANGE IT\n      - name: http              # default \u0026 DON'T CHANGE IT\n        protocol: TCP           # default \u0026 DON'T CHANGE IT\n        external: 8888          # default \u0026 DON'T CHANGE IT\n        internal: 8888          # default \u0026 DON'T CHANGE IT\n        pod: 0                  # default \u0026 DON'T CHANGE IT\n```\n\n### 数据库导出或备份\n\n命令行工具`ojtool`提供导出功能（需要在平台启动后使用）\n\n```shell\n./ojtool export [-dir \u003cexport folder\u003e]\n```\n\n通过 `-dir` 指定导出到的目录，默认导出到`data`目录\n\n### 用户批量导入\n\n命令行工具`ojtool`提供从csv文件批量导入用户的功能（需要在平台启动后使用）\n\n```shell\n./ojtool load -welcome=\u003ctrue/false\u003e \u003ccsv file\u003e\n```\n\n- `welcome`参数用于指定在导入用户后是否向用户发送初始化邮件\n- `\u003ccsv file\u003e`为包含需要导入的用户数据的csv文件，格式为`\u003cmail\u003e,\u003cusername\u003e`，不含表头\n\n## 开发指南\n\n### 前端\n\n- **请在`app`目录下打开vscode**\n- **npm相关命令，请在`app`目录下运行**\n- 总之请将`app`做为工作目录\n\n### 后端\n\n- **使用Goland将`server`作为工作目录**\n\n### 接口相关\n\n- GraphQL的schema文件定义在`server/services/schema.graphql`目录下\n  \n- 请求成功返回message 为空字符串（没有消息就是好消息）\n  \n\n### 构建\n\n前端环境准备：依赖latest版本的npm和nodejs，在app目录下执行\n\n```shell\nnpm install #必要情况下可以删除package-lock.json\n```\n\n如需要参与开发（向公开的git提交代码），请配置好自动lint，即在app目录下执行\n\n```shell\nnpm run prepare\n```\n\n构建命令行工具和前后端一体化可执行文件：依赖golang，在server目录下执行\n\n```shell\ngo run build.go --postgres  #postgres版本\ngo run build.go --sqlite    #sqlite版本暂时弃用\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FTp0t-Team%2FTp0tOJ","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FTp0t-Team%2FTp0tOJ","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FTp0t-Team%2FTp0tOJ/lists"}