2023kalmarctf复现
2023kalmarctf
crypto
BabyOneTimePad
1 | #!/usr/bin/env python3 |
整个代码就是需要猜出password,第一次给出与key的异或结果,第二次我们可以控制password的置换位置结果,只要是permutation包含0-127的数字都存在就行,也就是说可以传入前面全部都是0,后面是1-127使得传入的password都是第一个字节,那么思路上面很简单。
那么只要恢复key和由key以及密文就可以解出明文
1 | def encrypt_otp(cleartext, key = os.urandom(PASS_LENGTH_BYTES)): |
这个函数就是加密函数
那么如果我们知道cleartext,那么就可以知道key,因为
1 | encrypt_otp(t,c2)==key*2 # 其中t是password第一个字节*128,c2是第二次加密密文 |
然后写出解密函数(假如知道key 的情况下面)
1 | def decrypt_otp(ciphertext, key): |
这时候我们只要通过爆破password的第一个字节,产生的key尝试去解出password就行了
1 | c1='35514e2c9d23bc0e0659b606173b5b4073985fa90e9aefe75b28fbaa0e8f7730ba242b9236b9fd58f65d4de7ce0c07befa8c14c8bd66035e1e26ddeb16ceae53741d18a15814e1b8cdc1fd4a932dd9ff792579a872864e98b56958a8f236a6f191e54d459a6430435cc11080fda483cb1ba5672ab5d6d96411b9c6c78d15988330011b7c9a29ba0b030eb151176a091579cf59aa5dccebe25a2caeab0f8e706cbf777e9737eda103a10c46e6990b5fbba88b15cae736570e1b7f8cbc47cbab05754d1ef70449b3b8c995fe1d932989ab7d7722a572861c9be2385efdf06df8aa99b71a17c833374f5ccf14d4a8f9869f43f53621e4d9d93246b09492df159fdb' |
那么就可以解出password了
注意:
1 | for i,x in enumerate(cleartext.hex().encode()) |
会导致加密的长度发生变化
另外就是Permutation的零的个数问题,可能当时传少了,导致本地数据打通了,一打远程就炸。
还有就是如果利用pwntools打远程的话会导致try失效,所以本地跑脚本会让在解密出现不是utf-8编码的时候返回error,这样才能解密(大概是我的问题???)
EasyOneTimePad
在上一题的基础上,修复了非预期。
1 | assert len(permutation) == PASS_LENGTH_BYTES #确保长度 |
那么,整个计算采用了两次密钥参与计算,计算上只是简单的异或,那么可以使用sage的多项式环解线性方程,或者使用z3约束求解(主要只是异或操作,容易求解)
KalmarCTF 2023 WriteUps | 廢文集中區 (maple3142.net)
1 | def encrypt_otp_z3(pt_hex, key): |
1 | pt_hex_sym = [BitVec(f"pt_hex_{i}", 8) for i in range(PASS_LENGTH_BYTES * 2)] |
确保明文是16进制数
1 | pt_sym = [(x, y) for x, y in zip(pt_hex_sym[::2], pt_hex_sym[1::2])] #合并形成两个16字节 |
然后添加条件,加密和密文相同
解出结果
1 | m[x].as_long() |
这个代码片段是在求解器的模型 m
中获取一个符号变量 x
的具体值。m[x]
表示 x
在模型中的值,它是一个 BitVecRef
对象,表示一个二进制位向量。而 as_long()
方法则将这个二进制位向量转换为对应的整数值。
然后转为16进制就行了
官方解法:kalmarctf-2023/solve.py at main · kalmarunionenctf/kalmarctf-2023 (github.com)
1 | 官方的思想是,利用阶为2的环解线性方程组 |
本地调试:
1 | remote = pwnlib.tubes.process.process('./challenge.py') |
misc
kalmarunionen-fun
题目给出了传感器的xyz轴的加速度。
这题属实是从来没有涉及过的
那么就简单跟着官方wp学习一下数据处理就好了。
1 | import sys |
整个传感器的加速度由于现实上的测量无法做到平稳,需要一定的时间的数据才能认定该方向上的数据成立
将x,y的加速度提取出来。使用darr储存。跟踪conseq0和conseq1变量中零和一的连续运行长度。如果检测到至少4个时间步长的零运行,并且信号电平当前很高(curlvl=1),并且距离检测到最后一个符号已经有足够的时间((i-lastlvlchange)>23),脚本假定一个符号刚刚结束并开始一个新符号。它增加signnum变量以跟踪到目前为止检测到的符号数量,并打印出有关刚刚结束的符号的长度和时间的信息。它还将与该符号对应的加速度计数据写入一个新文件,该文件的名称基于命令行上提供的前缀。 如果检测到至少4个时间步长的运行,并且信号电平当前为低(curlvl=0),则脚本假定新符号刚刚开始并将curlvl设置为1。
之后的就是通过噪声定位物体https://github.com/balzer82/Kalman/blob/master/Kalman-Filter-CA.ipynb
来生成过滤脚本,画出二维,记录最大的x和最小的x,如果之间的差距过大,记为0,否则记为1,然后转ascii即可
ps:前五个数据不要,本来手动跑的,预判第一个字符是k,但是复现出不来还怀疑是我的问题
forensic
lleHSyniT!
1 | strings proc.dmp | grep -i kalmar |
cards
tcp 79流之后提取出字符
1 | for i in {79..157}; do tshark -r cards.pcap -qz follow,tcp,raw,$i | awk '{a[NR]=$0}END{gsub(/^[ \t]+|[ \t]+$/, "", a[NR-1]); printf "%s ", a[NR-1]}' >> output.txt;done |
解释:
1 | tshark -r cards.pcap -qz follow,tcp,raw,$i |
1 | awk '{a[NR]=$0}END{gsub(/^[ \t]+|[ \t]+$/, "", a[NR-1]); printf "%s ", a[NR-1]}' |
因此,这个 AWK 命令基本上逐行读取输入,将每行存储在一个数组中,从输入的倒数第二行中删除开头和结尾的空格字符,然后打印修改后的倒数第二行,并带有一个空格字符。
打乱的字符通过长度为135的ftp获得cwd目录顺序
1 | tshark -r cards.pcap -Y "frame.len == 135 and ftp.current-working-directory" -T fields -e ftp.current-working-directory |
tshark
:启动 tshark 工具。-r cards.pcap
:指定要读取的 pcap 文件的路径和名称。-Y "frame.len == 135 and ftp.current-working-directory"
:使用 Wireshark 显示过滤器语法来筛选符合条件的数据包。frame.len == 135
表示数据包长度为 135,这是某些 FTP 客户端发送 PWD 命令时的预期长度。ftp.current-working-directory
表示匹配包含 FTP 工作目录的数据包。-T fields
:指定输出格式为字段形式。-e ftp.current-working-directory
:指定要提取的字段,这里是 FTP 工作目录。
写脚本重排就行了
1 | m = 'm_tfwr_flf_3eccaykdw_hhuhrld{erae\n_onsuo}04afr__ar_u1ut_ksffklas_hsce33f_e3p_hn' |
sewing-waste-and-agriculture-leftovers
1 | tshark -2 -r swaal.pcap -R "udp.port==9999" -T fields -e data | tr -d '\n' | fold -w 120 | while read l; do echo "$l" | xxd -r -p | tr -d '\n'; echo; done > raw_bytes.txt |
-R "udp.port==9999"
:使用 Wireshark 显示过滤器语法来筛选符合条件的数据包。这里表示仅提取 UDP 端口为 9999 的数据包。-T fields
:指定输出格式为字段形式。-e data
:指定要提取的字段,这里是 UDP 数据包的数据字段。tr -d '\n'
:使用tr
命令删除输出中的所有换行符,以便将所有提取的数据合并到一行中。fold -w 120
:使用fold
命令将长行分成每行 120 个字符。这样做是为了在输出中使数据更易读。while read l; do
:使用while
循环读取每个行,并将其存储在变量l
中。echo "$l" | xxd -r -p
:使用xxd
命令将十六进制字符串转换为原始字节。tr -d '\n'
:使用tr
命令删除输出中的所有换行符,以便将所有字节合并到一行中。echo
:使用echo
命令输出一个换行符,以便将每行输出到不同的行中。done > raw_bytes.txt
:将输出重定向到文件raw_bytes.txt
中。
1 | for i in {1..58}; do cat raw_bytes.txt | cut -b "$i" | sort -u | tr -d '\n'; done; echo |
cut -b "$i"
:使用 cut 命令提取一个字符串的第 i 个字节。sort -u
:使用 sort 命令对提取的字节进行排序并去重。-u
选项告诉 sort 命令只输出不同的行。tr -d '\n'
:使用 tr 命令删除输出中的所有换行符,以便将所有去重的字节合并到一行中。
web
caddy
docker文件:
1 | version: '3.7' |
其中将flag复制到backups,删除了flag.txt
1 | command: 指定在启动容器时要运行的命令。它使用"sh -c"运行一系列命令。 |
php.caddy.chal-kalmarc.tf/flag.txt已经被删除,那么就是要访问到备份文件/backups/..../flag.txt,
根据https://github.com/caddyserver/caddy/pull/4407
也就是说存在可以访问/backups/…/flag.txt的方法,只需要抓包修改url访问//flag.txt和host为/backups/php.caddy.chal-kalmarc.tf即可
1 | curl --path-as-is -k --resolve php.caddy.kalmarc.tf:443:172.18.0.1 http://php.caddy.kalmarc.tf//flag.txt -H 'Host: backups/php.caddy.kalmarc.tf' |
Invoiced
1 | 用meta来重定向..... 学习了 |