-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(ppc/leet-machine): 添加题目、题解 (#38)
- Loading branch information
Showing
8 changed files
with
413 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
name: Challenge 1337 Machine | ||
|
||
on: | ||
push: | ||
branches: ["main", "ppc/leet-machine"] | ||
paths: | ||
- "!**/README.md" | ||
- "challenges/ppc/leet_machine/build/**" | ||
workflow_dispatch: | ||
|
||
env: | ||
TYPE: ppc | ||
NAME: leet_machine | ||
REGISTRY: ghcr.io | ||
|
||
jobs: | ||
challenge-build: | ||
runs-on: ubuntu-latest | ||
permissions: | ||
contents: read | ||
packages: write | ||
steps: | ||
- name: Checkout repository | ||
uses: actions/checkout@v4 | ||
|
||
- name: Set up Docker Buildx | ||
uses: docker/setup-buildx-action@v3 | ||
|
||
- name: Log in to the Container registry | ||
uses: docker/login-action@v3 | ||
with: | ||
registry: ${{ env.REGISTRY }} | ||
username: ${{ github.actor }} | ||
password: ${{ secrets.GITHUB_TOKEN }} | ||
|
||
- name: Extract metadata (tags, labels) for Docker | ||
id: meta | ||
uses: docker/metadata-action@v5 | ||
with: | ||
images: ${{ env.REGISTRY }}/${{ github.repository }}/${{ env.NAME }} | ||
tags: | | ||
latest | ||
- name: Build and push Docker image | ||
uses: docker/build-push-action@v6 | ||
with: | ||
context: challenges/${{ env.TYPE }}/${{ env.NAME }}/build | ||
tags: ${{ steps.meta.outputs.tags }} | ||
labels: ${{ steps.meta.outputs.labels }} | ||
push: true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,208 @@ | ||
--- | ||
title: 1337 Machine | ||
author: 13m0n4de | ||
difficulty: Normal | ||
category: PPC | ||
image: ghcr.io/svuctf/svuctf-helloworld-2024/leet_machine:latest | ||
port: 70 | ||
writeup_author: 13m0n4de | ||
tags: | ||
- pwntools | ||
reference: | ||
--- | ||
|
||
# 1337 Machine | ||
|
||
## 题目描述 | ||
|
||
教学楼的某个角落,你发现了一台古老而神秘的设备 —— 1337 Machine,这台机器曾经是顶尖的语言转换装置,能将普通文本完美地转化为 [Leet](https://zh.wikipedia.org/zh-cn/Leet) 文本。 | ||
|
||
然而,岁月的侵蚀使得这台机器变得极度不稳定。它的电路板正在冒烟,显示屏闪烁不定,发出令人不安的嗡鸣声。 | ||
|
||
突然,机器苏醒了!它开始疯狂地生成随机英文句子,仿佛在寻求某种解脱。屏幕上闪现出一条紧急消息: | ||
|
||
「系统不稳定,需要立即进行 100 次成功的 Leet 转换以稳定 Flag 核心。」 | ||
|
||
> Hint: Leet(英文中亦称 leetspeak 或 eleet。Leet 拼写法:L337, 3L337, 31337 或 1337),又称黑客语,是指一种发源于西方国家的 BBS、在线游戏和黑客社群所使用的文字书写方式。通常是把拉丁字母转变成数字或是特殊符号,例如 E 写成 3、A 写成 @ 等。或是将单字写成同音的字母或数字,如 to 写成 2、for 写成 4 等等。 | ||
## 题目解析 | ||
|
||
### 分析题目 | ||
|
||
这是一道自动化编程题目,需要与远程服务进行交互。根据题目描述,这是一台需要修复的 1337 (Leet) 转换机器。 | ||
|
||
连接到远程服务后,机器会提供一个明确的转换规则表: | ||
|
||
``` | ||
$ nc <IP> <PORT> | ||
.---------. | ||
|.-------.| | ||
||>1337# || | ||
|| || | ||
|"-------'| | ||
.-^---------^-. | ||
| ---~ ---~ | | ||
"-------------' | ||
================================================== | ||
1337 Machine 唤醒中... | ||
================================================== | ||
系统日志: 检测到核心不稳定 | ||
系统日志: 启动紧急修复程序 | ||
系统日志: 需要执行 100 次成功的 LEET 转换以重新校准系统 | ||
系统日志: 检测到转换模块配置如下 | ||
a -> 4 | ||
e -> 3 | ||
g -> 6 | ||
i -> 1 | ||
o -> 0 | ||
s -> 5 | ||
t -> 7 | ||
按下回车键开始系统修复... | ||
``` | ||
|
||
题目的核心机制很直观: | ||
|
||
- 机器会随机生成英文句子 | ||
- 我们需要按照给定的规则将这些句子转换为 Leet 格式 | ||
- 这个过程需要成功完成 100 次才能获取 flag,并且有时间限制 | ||
- 每次转换都必须准确,任何错误都会导致程序终止 | ||
|
||
### 编写自动化脚本 | ||
|
||
由于需要处理 100 次转换,手动操作显然不切实际,需要编写脚本自动化整个过程。 | ||
|
||
使用 Python 的 [pwntools](https://docs.pwntools.com/en/stable/) 库编写解题脚本是最直接的方法。这个库提供了便捷的远程交互功能,比较适合这类 PPC 题目。 | ||
|
||
#### 第一步:建立连接 | ||
|
||
使用 pwntools 连接到远程服务: | ||
|
||
```python | ||
from pwn import * | ||
|
||
io = remote("localhost", 1337) | ||
``` | ||
|
||
这里使用 pwntools 的 `remote` 函数创建了一个连接对象,用于与服务器进行交互。 | ||
|
||
#### 第二步:实现转换规则 | ||
|
||
根据题目给出的转换规则,我们需要将特定字母转换为数字。可以用字典来存储这些映射关系: | ||
|
||
```python | ||
LEET_DICT = {"a": "4", "e": "3", "g": "6", "i": "1", "o": "0", "s": "5", "t": "7"} | ||
``` | ||
|
||
然后编写一个转换函数: | ||
|
||
```python | ||
def convert_to_leet(sentence): | ||
return "".join(LEET_DICT.get(char, char) for char in sentence) | ||
``` | ||
|
||
这个函数使用了字典的 `get` 方法,如果字符在字典中存在就进行转换,否则保持原字符不变。使用列表推导式和 `join` 方法可以简洁地完成字符串转换。 | ||
|
||
#### 第三步:处理初始化过程 | ||
|
||
程序开始时需要等待启动提示并发送回车: | ||
|
||
```python | ||
io.recvuntil("按下回车键开始系统修复...".encode()) | ||
io.sendline(b"") | ||
``` | ||
|
||
#### 第四步:实现主要转换循环 | ||
|
||
接下来是主要的转换循环,需要重复 100 次: | ||
|
||
```python | ||
for _ in range(100): | ||
# 接收待转换的文本 | ||
io.recvuntil("输入文本: ".encode()) | ||
sentence = io.recvline().decode().strip() | ||
log.info(sentence) | ||
|
||
# 转换文本 | ||
leet_sentence = convert_to_leet(sentence) | ||
log.info(leet_sentence) | ||
|
||
# 发送转换结果 | ||
io.sendlineafter("输出 LEET 文本 > ".encode(), leet_sentence.encode()) | ||
|
||
# 检查响应 | ||
response = io.recvline().decode() | ||
if "转换成功" not in response: | ||
log.failure(f"转换失败: {response}") | ||
break | ||
|
||
# 打印分隔线方便查看 | ||
log.info("-" * 40) | ||
``` | ||
|
||
#### 第五步:获取 flag | ||
|
||
完成转换后进入交互模式以获取 flag: | ||
|
||
```python | ||
io.interactive() | ||
``` | ||
|
||
### 完整脚本 | ||
|
||
[solve.py](./writeup/solve.py) | ||
|
||
```python | ||
from pwn import * | ||
|
||
io = remote("localhost", 1337) | ||
|
||
LEET_DICT = {"a": "4", "e": "3", "g": "6", "i": "1", "o": "0", "s": "5", "t": "7"} | ||
|
||
|
||
def convert_to_leet(sentence): | ||
return "".join(LEET_DICT.get(char, char) for char in sentence) | ||
|
||
|
||
io.recvuntil("按下回车键开始系统修复...".encode()) | ||
io.sendline(b"") | ||
|
||
for _ in range(100): | ||
io.recvuntil("输入文本: ".encode()) | ||
sentence = io.recvline().decode().strip() | ||
log.info(sentence) | ||
|
||
leet_sentence = convert_to_leet(sentence) | ||
log.info(leet_sentence) | ||
|
||
io.sendlineafter("输出 LEET 文本 > ".encode(), leet_sentence.encode()) | ||
response = io.recvline().decode() | ||
if "转换成功" not in response: | ||
log.failure(f"转换失败: {response}") | ||
break | ||
|
||
log.info("-" * 40) | ||
|
||
io.interactive() | ||
``` | ||
|
||
### 运行效果 | ||
|
||
脚本运行的输出大致如下: | ||
|
||
``` | ||
[*] ---------------------------------------- | ||
[*] Red general similar someone peace question even. | ||
[*] R3d 63n3r4l 51m1l4r 50m30n3 p34c3 qu35710n 3v3n. | ||
[*] ---------------------------------------- | ||
[*] Dark standard guy read. | ||
[*] D4rk 574nd4rd 6uy r34d. | ||
[*] ---------------------------------------- | ||
[*] Switching to interactive mode | ||
系统状态: 100/100 | ||
系统日志: 修复成功,Flag 核心已稳定 | ||
Flag: flag{test_flag} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
FROM ghcr.io/svuctf/base-v2/python-xinetd:latest | ||
|
||
WORKDIR /app | ||
|
||
COPY machine.py machine.py | ||
COPY requirements.txt requirements.txt | ||
|
||
COPY xinetd.conf /etc/xinetd.conf | ||
COPY --chmod=500 init.sh /init.sh | ||
|
||
RUN pip install -r requirements.txt | ||
|
||
CMD ["xinetd", "-dontfork"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
#!/bin/sh | ||
|
||
python -u /app/machine.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import os | ||
import random | ||
import time | ||
from faker import Faker | ||
|
||
LEET_DICT = {"a": "4", "e": "3", "g": "6", "i": "1", "o": "0", "s": "5", "t": "7"} | ||
FLAG = os.environ.get("GZCTF_FLAG", "flag{test_flag}") | ||
|
||
fake = Faker() | ||
|
||
ASCII_ART = """ | ||
.---------. | ||
|.-------.| | ||
||>1337# || | ||
|| || | ||
|"-------'| | ||
.-^---------^-. | ||
| ---~ ---~ | | ||
"-------------' | ||
""" | ||
|
||
|
||
def generate_random_sentence(): | ||
return fake.sentence(nb_words=random.randint(3, 10), variable_nb_words=True) | ||
|
||
|
||
def convert_to_leet(sentence): | ||
return "".join(LEET_DICT.get(char, char) for char in sentence) | ||
|
||
|
||
def display_rules(): | ||
print("系统日志: 检测到转换模块配置如下") | ||
for key, value in LEET_DICT.items(): | ||
print(f"{key} -> {value}") | ||
print() | ||
|
||
|
||
def play_round(): | ||
sentence = generate_random_sentence() | ||
correct_leet = convert_to_leet(sentence) | ||
|
||
print(f"输入文本: {sentence}") | ||
start_time = time.time() | ||
user_input = input("输出 LEET 文本 > ") | ||
end_time = time.time() | ||
|
||
if end_time - start_time > 3: | ||
print("系统警告: 响应超时") | ||
return False | ||
|
||
if user_input == correct_leet: | ||
print("转换成功: 系统稳定性 +1") | ||
return True | ||
|
||
print("转换失败: 系统不稳定性增加") | ||
return False | ||
|
||
|
||
def main(): | ||
print(ASCII_ART) | ||
print("=" * 50) | ||
print("1337 Machine 唤醒中...") | ||
print("=" * 50) | ||
time.sleep(1) | ||
print("系统日志: 检测到核心不稳定") | ||
print("系统日志: 启动紧急修复程序") | ||
print("系统日志: 需要执行 100 次成功的 LEET 转换以重新校准系统") | ||
time.sleep(1) | ||
display_rules() | ||
|
||
input("按下回车键开始系统修复...") | ||
|
||
successful_conversions = 0 | ||
for i in range(1, 101): | ||
print(f"\n系统日志: 开始第 {i} 次转换") | ||
if play_round(): | ||
successful_conversions += 1 | ||
print(f"系统状态: {successful_conversions}/100") | ||
else: | ||
print("\n系统日志: 修复失败") | ||
print(f"系统日志: 仅完成 {successful_conversions}/100 次成功转换") | ||
print("系统日志: 进入紧急休眠模式") | ||
return | ||
|
||
print("\n系统日志: 修复成功,Flag 核心已稳定") | ||
print(f"Flag: {FLAG}") | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Faker==30.6.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
service ctf | ||
{ | ||
disable = no | ||
id = xinetd | ||
socket_type = stream | ||
protocol = tcp | ||
wait = no | ||
user = root | ||
type = UNLISTED | ||
port = 1337 | ||
bind = 0.0.0.0 | ||
server = /init.sh | ||
# safety options | ||
per_source = 10 # the maximum instances of this service per source IP address | ||
rlimit_cpu = 20 # the maximum number of CPU seconds that the service may use | ||
rlimit_as = 100M # the Address Space resource limit for the service | ||
} |
Oops, something went wrong.