Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions crypto/[GCCCTF 2025]伊莫鸡.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
## 基本信息

- 题目名称: [GCCCTF 2025]伊莫鸡
- 考点清单: HTML实体编码、换表base64

## 一、看到什么

全是表情符号,还有一串64个不重复的字符表

## 二、想到什么解题思路

表情符号可以想到base100,64个不重复的字符表可能是换表base64的对应的表

## 三、尝试过程和结果记录

表情符号在网站上解密可以得到第一个字符串,base100
给出两串字符串:

1. `lWEQ...;yW` —— HTML 实体编码。
2. `a6pQqIUurcibPENJSlHxjZkydT3tgY4oFeDXL5mf1V+zR7wv9CnBWh/M2sOAG80K` —— 64 个字符且不重复。
提示说明第二串为“非标准 Base64”,意味着它是**自定义 Base64 字母表**。

**解题思路**

1. 首先将 HTML 实体转回字符,得到:
`lWEQShlU4hgBPjP9xxEoEB6oELEQSBYUyBr9PXjeryW`。
2. 观察第二串长度正好 64,推测为自定义 Base64 字母表。
标准 Base64 表为:`A–Z a–z 0–9 + /`。
3. 将自定义表的每个字符按索引映射回标准 Base64 表。
4. 对第一串中字符逐一替换为对应标准 Base64 字符。
5. 对替换结果做标准 Base64 解码,得到明文。

**关键代码**

```python
import base64, html
html_text = "lWEQShlU4hgBPjP9xxEoEB6oELEQSBYUyBr9PXjeryW"
custom = "a6pQqIUurcibPENJSlHxjZkydT3tgY4oFeDXL5mf1V+zR7wv9CnBWh/M2sOAG80K"
standard = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
mapping = {custom[i]: standard[i] for i in range(64)}
decoded = html.unescape(html_text)
mapped = "".join(mapping.get(ch, ch) for ch in decoded)
print(base64.b64decode(mapped + "=" * ((4 - len(mapped) % 4) % 4)).decode())
```

**flag**

```
GCCCTF{W31C0M3_70_6CCC7F_2025!!}
```

323 changes: 323 additions & 0 deletions crypto/[GCCCTF 2025]密钥危机.md

Large diffs are not rendered by default.

220 changes: 220 additions & 0 deletions reverse/[GCCCTF 2025] constraint.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
## 基本信息

- 题目名称: [GCCCTF 2025] constraint
- 考点清单: 脱壳技术,约束求解

## 一、看到什么

这是一道综合性的Reverse题目,涉及壳识别、脱壳、静态分析和约束求解。题目给出一个Windows PE 64位可执行文件calme.exe。

## 二、想到什么解题思路

使用IDA Pro打开calme.exe,发现以下特征:

1. 程序段名为 **UPX0**、**UPX1**、**UPX2**
2. 入口点函数非常复杂,包含大量位操作和解压逻辑
3. 代码段中有大量0xFF填充

这些特征表明程序使用了 **UPX壳** 进行压缩。

## 三、尝试过程和结果记录

UPX是一个常见的可执行文件压缩工具,可以直接使用UPX工具进行解包:

```bash
# 安装UPX(macOS)
brew install upx

# 解包
upx -d calme.exe -o calme_unpacked.exe
```

**解包结果**:

- 原始大小:137KB (140592 bytes)
- 解包后:311KB (318256 bytes)
- 压缩率:44.18%

解包成功后,在IDA Pro中打开`calme_unpacked.exe`进行分析。

### 静态分析

使用IDA Pro分析解包后的程序,找到两个关键函数:

#### main函数 (0x4016ee)

```c
int main(int argc, const char **argv, const char **envp)
{
char Buffer[272];
FILE *v3;

printf("Enter the flag: ");
v3 = __acrt_iob_func(0);

if (!fgets(Buffer, 256, v3))
return 1;

// 去除换行符
size_t len = strlen(Buffer);
if (len > 0 && Buffer[len-1] == '\n')
Buffer[len-1] = '\0';

if (verify_flag(Buffer)) {
puts("Correct!");
return 0;
} else {
puts("Wrong!");
return 1;
}
}
```

#### verify_flag函数 (0x401560)

这是核心验证函数,包含多个约束条件:

```c
bool verify_flag(char* a1)
{
unsigned char v5, v6, v7, v8, v9, v10, v11, v12;

// 1. 检查长度:必须为16字符
if (strlen(a1) != 16)
return false;

// 2. 检查前缀:必须是"GCCCTF{"
if (memcmp(a1, "GCCCTF{", 7) != 0)
return false;

// 3. 检查结尾:必须是'}'
if (a1[15] != '}')
return false;

// 4. 提取8个字节 (flag[7]到flag[14])
for (int i = 0; i < 8; i++)
*(&v5 + i) = a1[i + 7];

// 5. 线性方程组约束
if (7*v5 + 3*v6 + 11*v7 + 13*v9 != 2145)
return false;
if (8*v6 + 12*v8 + 9*v10 + 4*v12 != 2491)
return false;
if (6*v5 + 5*v7 + 8*v11 + 7*v12 != 2299)
return false;
if (9*v8 + 11*v9 + 6*v10 + 10*v11 != 3165)
return false;

// 6. 异或约束
return (v10 ^ (v5 ^ v6 ^ v7 ^ v8 ^ v9)) == 95;
}
```

### 建立数学模型

从验证函数中提取出约束条件:

**变量定义**:

- Flag格式:`GCCCTF{xxxxxxxx}`
- 需要求解8个字节:v5, v6, v7, v8, v9, v10, v11, v12

**约束条件**:

1. **线性方程组**(4个方程,8个未知数):

```
7*v5 + 3*v6 + 11*v7 + 0*v8 + 13*v9 + 0*v10 + 0*v11 + 0*v12 = 2145
0*v5 + 8*v6 + 0*v7 + 12*v8 + 0*v9 + 9*v10 + 0*v11 + 4*v12 = 2491
6*v5 + 0*v6 + 5*v7 + 0*v8 + 0*v9 + 0*v10 + 8*v11 + 7*v12 = 2299
0*v5 + 0*v6 + 0*v7 + 9*v8 + 11*v9 + 6*v10 + 10*v11 + 0*v12 = 3165
```

2. **异或约束**:

```
v10 ^ (v5 ^ v6 ^ v7 ^ v8 ^ v9) = 95
```

3. **字符范围约束**:

- 所有变量必须是可打印ASCII字符(33-126)

### 编写求解脚本

使用Z3 SMT求解器来求解约束题目

```python
#!/usr/bin/env python3
"""
CTF题目解题脚本
使用Z3 SMT求解器来求解约束题目
"""

from z3 import *

def solve_constraint_challenge():
"""使用Z3求解约束系统"""

print("=== Constraint Solver ===")

# 创建Z3求解器
solver = Solver()
solver.set("timeout", 10000)

# 定义8个整数变量
b = [Int(f'b{i}') for i in range(8)]

# 字符类型约束
for i in range(8):
solver.add(Or(
And(b[i] >= 97, b[i] <= 122), # 小写字母
And(b[i] >= 65, b[i] <= 90), # 大写字母
And(b[i] >= 48, b[i] <= 57) # 数字
))

# 线性约束(每个约束至少4个变量)
solver.add(7*b[0] + 3*b[1] + 11*b[2] + 13*b[4] == 2145)
solver.add(8*b[1] + 12*b[3] + 9*b[5] + 4*b[7] == 2491)
solver.add(6*b[0] + 5*b[2] + 8*b[6] + 7*b[7] == 2299)
solver.add(9*b[3] + 11*b[4] + 6*b[5] + 10*b[6] == 3165)

# XOR约束(使用位操作约束,确保XOR约束是必需的)
# 为前6个字节的每一位创建布尔变量
bits = [[Bool(f'bit_{i}_{j}') for j in range(8)] for i in range(6)]

# 约束:位变量组合成字节值
for i in range(6):
byte_val = Sum([If(bits[i][j], 2**j, 0) for j in range(8)])
solver.add(b[i] == byte_val)

# XOR约束:每一位的XOR结果等于目标值的对应位
target_xor = 0x5f
target_bits = [bool(target_xor & (1 << j)) for j in range(8)]
for j in range(8):
bit_xor = bits[0][j]
for i in range(1, 6):
bit_xor = Xor(bit_xor, bits[i][j])
solver.add(bit_xor == target_bits[j])

# 求解
if solver.check() == sat:
model = solver.model()
flag_bytes = [model[b[i]].as_long() for i in range(8)]
flag_content = ''.join(chr(b) for b in flag_bytes)
return f"GCCCTF{{{flag_content}}}"
else:
return None

def main():
flag = solve_constraint_challenge()
if flag:
print(flag)
else:
print("No solution found")

if __name__ == "__main__":
main()
```

flag: **GCCCTF{F14gH3re}**
96 changes: 96 additions & 0 deletions web/[GCCCTF 2025]守法公民.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
## 基本信息

- 题目名称: [GCCCTF 2025]守法公民
- 考点清单: js分析,越权漏洞

## 一、看到什么

进到题目是一个网络安全法知识问答平台,给出提示达到10000分就可以解锁大奖,但是每题才一分,要是直接答题肯定是来不及的。

先注册一个账号:

可以发现所有发送到后端的数据都是加密的,格式为:

```json
{
“data”:“xxxxxxxxxxxxxxxxxx”
}
```

## 二、想到什么解题思路

点开浏览器调试页面发现js代码混淆,我们要先分析加密逻辑。

## 三、尝试过程和结果记录

调试页面有反调试,可以通过“Nerver pause here”把这些debugger失效

![image-20251103105905177](images/[GCCCTF 2025]守法公民-1.png)

尝试重置密码,然后发现有一个函数名aesEncrypt,尝试在此打断点

![image-20251103110250612](images/[GCCCTF 2025]守法公民-2.png)

可以看到传入的数据是:

```
{\"user_id\":\"8f599954593c4970b28dcac52ff9df71\",\"new_password\":\"1\",\"confirm_password\":\"1\"}
```

可能是重置密码时需要带上本人id,以及重置的密码然后发送给后端,这里可能存在越权漏洞。

在这里发现了CryptoJS的加密部分:

![image-20251103110605697](images/[GCCCTF 2025]守法公民-3.png)

一般格式是这样的:

```
const ciphertext = CryptoJS.AES.encrypt(data, key, {
iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
```

这里需要key,data,iv,mode,padding

对照混淆代码看哪里是,然后去console打印

_`_0x2d4799`对应data,_`_0x41afe6`对应key,`_0x304595`对应iv,`CryptoJS['mode']['CBC']`就是mode,`CryptoJS['pad'][_0x3a81be(0x166)]`是padding

依次去打印,注意iv和key是字节,可以base64编码一下再输出

![image-20251103111828675](images/[GCCCTF 2025]守法公民-4.png)

然后就可以据此构造加密数据包修改管理员密码,现在还差一个管理员的uuid才能构造。

想到公告栏是管理员发的,查看网页源代码,

![image-20251103112134612](images/[GCCCTF 2025]守法公民-5.png)

由此可以开始构造包,先抓一个修改密码的包解密:

```javascript
JSON.parse(CryptoJS.AES.decrypt("ReZVa6ElTwQSlWYY5IlkbKE6VFBNyMc4KROEd1eVTV4rDn2Y1lc5LB6hOPv2w1YGqviCVP1kQJPI9dWxtfl1dkBeew5PcKzqDxexmT/HpAyH8vLrr2JBgTLJq5XyzPFO", CryptoJS.enc.Base64.parse("Qqk39LaSxFml8cwZLxFNCk1NXun6i2nXebB2rouRgNA="), {iv: CryptoJS.enc.Base64.parse("F1wyy3xQ42vTbm2JZ+6yow=="),mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.Pkcs7}).toString(CryptoJS.enc.Utf8));
```

然后修改uuid和password,构造以下加密包:

```javascript
CryptoJS.AES.encrypt(JSON.stringify({user_id: '722e87fa398e4faeb228fe27d6b2a7a6', new_password: '2', confirm_password: '2'}), CryptoJS.enc.Base64.parse("Qqk39LaSxFml8cwZLxFNCk1NXun6i2nXebB2rouRgNA="), {iv: CryptoJS.enc.Base64.parse("F1wyy3xQ42vTbm2JZ+6yow=="), mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7}).toString();
```

![image-20251103112508998](images/[GCCCTF 2025]守法公民-6.png)

把这个发过去:

fFtkxNNsnuawsN2sm453BBWFPrv5dBY/rNGZBu9w1xJ4G2jta74pxOIFOc607+rMM8D+OIrDcP6i16QCkjHfO2P1UkMc2j0rXJcOAWQ33irprAdmLjAFKdj2M+pfVPaz

![image-20251103112632563](images/[GCCCTF 2025]守法公民-7.png)

然后用 admin/2 登录

有一个分数设置,设置一个比较高的分数,然后随便答题就可以拿到flag了

![image-20251103112755005](images/[GCCCTF 2025]守法公民-8.png)
Binary file added web/images/[GCCCTF 2025]守法公民-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/images/[GCCCTF 2025]守法公民-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/images/[GCCCTF 2025]守法公民-3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/images/[GCCCTF 2025]守法公民-4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/images/[GCCCTF 2025]守法公民-5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/images/[GCCCTF 2025]守法公民-6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/images/[GCCCTF 2025]守法公民-7.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/images/[GCCCTF 2025]守法公民-8.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.