Mobile

极限逃脱

给了一个 IOS 安装包,解压反编译

搜索到字符串,交叉引用,跟踪到-[ViewController secondButtonClicked:]

简单分析,先对输入进行处理,然后开始构造正确的 flag,先提取出函数里的字符串

对获取到的字符串进行拼接,求出 SHA256 哈希值

接下来对于哈希值分段进行替换,注意几个值仔细对应一下就可以

最终是哈希值中取出五段进行分别替换(索引,长度):(1,8) (9,4) (5,4) (5,4) (5,12)

最终拿这替换后的五段哈希值与由输入分割出来的五段进行比较

exp

1
2
3
4
5
6
7
8
9
10
11
import hashlib
enc = b'{a67be199da4b-b092-bd3e-e777-a67be199da4b}'
table = hashlib.sha256(enc).hexdigest()
#6c9838a3c6810bdb2633ed5910b8547c09a7a4c08bf69ae3a95c5c37f9e8f57e
# 使用列表推导式和字符串切片简化处理
replacements = [('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'e'), ('e', 'f')]
parts = [table[1:9].replace(*replacements[0])] + [table[i:j].replace(*replacements[k])
for k, (i, j) in enumerate([(9, 13), (5, 9), (5, 9), (5, 17)], start=1)]
flag = 'ByteCTF{' + '-'.join(parts) + '}'
print(flag)
#ByteCTF{c9838b3c-6810-8a3d-8a3c-8a3c6810bdb2}

Reverse

ByteBuffer

ByteDance uses ByteXXX to name its products. For data transmission, ByteDance uses FlatBuffer based ByteBuffer, an extremely efficient and compact serialization technique. We have intercepted one of the traffic but can’t decode it. Can you analyze the ByteBuffer structure and recover something? flag is ByteCTF{recovered numbers}

二进制文件中有边和点的数据

边的数据形式如下,x 坐标是 0x77,y 坐标是 0x75(注意小端序),编号是 103

点的数据如下,x 坐标是 0x0640,y 坐标是 0x4B(同样是小端序),编号是120

提取数据,画图梭

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from PIL import Image, ImageDraw

f = open("ByteBuffer.bin", "rb").read()
edge, dot = [], {}
offsets = {'Edge': (4), 'Dot': (3)}

for key, (start_offset) in offsets.items():
i = 0
while True:
i = f.find(key.encode(), i + start_offset)
if i == -1:
break
x = int.from_bytes(f[i - 16 : i - 12], "little")
y = int.from_bytes(f[i - 12 : i - 8], "little")
if key == 'Edge':
ind = int(f[i + 6 : f.find(b"\x00", i + 6)])
edge.append([x, y])
else:
ind = int(f[i + 5 : f.find(b"\x00", i + 5)])
dot[ind] = (x, y)

img = Image.new("RGB", (1610, 150), (255, 255, 255))
draw = ImageDraw.Draw(img)

for data in edge:
try:
draw.line([dot[data[0]], dot[data[1]]], fill=(255, 0, 0), width=2)
except KeyError:
pass

img.save("ByteBuffer.png")
#ByteCTF{327542185069290715865}

babyAPK

使用 blutter 恢复符号表

记录一下流程

1
2
git clone https://github.com/worawit/blutter --depth=1
python .\scripts\init_env_win.py

管理员权限运行x64 Native Tools Command Prompt for VS 2022

进入 blutter 文件夹,提供 libapp.solibflutter.so 的路径

1
python blutter.py D:\CTF\WP\ByteCTF2024\Re\babyapk\lib\arm64-v8a .\output

记得开代理不然会报错

成功生成代码

然后用 output/ida_script/addNames.py恢复libapp符号表就行了

这里可以看到调用了babyapk_src_rust_api_simple_::m3N4B5V6_265088

librust_lib_babyapk搜索字符串

交叉引用一下,定位到sub_3AEE0

Z3 解方程就行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
from z3 import *
import uuid
enc = [0x0001EE59, 0x0000022A, 0x00001415, 0x00040714, 0x000013E0, 0x000008B8, 0xFFFDCEA0, 0x0000313B, 0x0003D798, 0xFFFFFE6B, 0x00000C4E, 0x00023884, 0x0000008D, 0x00001DB4, 0xFFFC1328, 0x00001EAC, 0x00043C64, 0x0000142B, 0xFFFFF622, 0x00023941, 0xFFFFEF6D, 0x0000120C, 0xFFFBD30F, 0x00001EBE, 0x00045158, 0xFFFFEF66, 0x00001D3F, 0x0004C46B, 0xFFFFF97A, 0x00001BFD, 0xFFFBA235, 0x00001ED2]
flag = ""
for i in range(len(enc) // 8):
x = [BitVec(f"x_{j}", 8) for j in range(8)]
s = Solver()
v45 = x[2]
v44 = x[3]
v46 = x[0]
v47 = x[1]
v48 = x[4]
v49 = x[5]
v50 = x[6]
v51 = x[7]
v52 = v46 * v49
v53 = v48 * v46
v41 = i * 8
s.add(v51 + v47 * v44 * v49 - (v46 + v50 + v45 * v48) == enc[v41])
s.add(v44 - v48 - v46 * v49 + v51 * v47 + v45 + v50 == enc[v41 + 1])
s.add(v52 - (v48 + v51 * v47) + v45 + v50 * v44 == enc[v41 + 2])
s.add(v47 + v48 * v46 - (v51 + v45) + v50 * v49 * v44 == enc[v41 + 3])
s.add(v49 * v44 + v47 + v45 * v48 - (v50 + v51 * v46) == enc[v41 + 4])
s.add(v52 + v47 * v44 + v45 - (v50 + v48 * v51) == enc[v41 + 5])
s.add(v51 - v47 + v45 * v49 + v50 - v53 * v44 == enc[v41 + 6])
s.add(v44 - v51 - (v47 + v49) + v53 + v50 * v45 == enc[v41 + 7])

for j in x:
s.add(j <= 126)
s.add(j >= 32)
if s.check() == sat:
m = s.model()
flag += "".join([chr(m[j].as_long()) for j in x])
flag = "Bytectf{" + str(uuid.UUID(flag)) + "}"
print(flag)
#Bytectf{32e750c8-fb21-4562-af22-973fb5176b9c}

ByteKit

One of our physical server have been hacked, we re-install the OS but there is still something there. Can you find it?

  1. execute “./run_qemu.sh” and login with username root and empty password
  2. after login, execute "./getflag.sh "
  3. it will reboot
  4. after reboot, re-login with root and empty password, execute "./getflag.sh " again, and you will get your flag.

给了一个 QEMU 镜像,需要先安装 QEMU 环境,可以看 qemu 安装与调试 进行安装

根据题目描述启动后,我们尝试 cat ./getflag.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#!/bin/bash
#两个 UUID,用于标识输入和输出的 EFI 变量。
BYTECTF_INPUT_GUID=93e91ed6-1a7a-46a1-b880-c5d281700ea2
BYTECTF_OUTPUT_GUID=93e91ed6-1a7a-46a1-b880-c5c281700ea2
#完整的文件路径,指向 EFI 变量文件。
BYTECTF_INPUT_VAR_FILE="/sys/firmware/efi/efivars/ByteCTFIn-$BYTECTF_INPUT_GUID"
BYTECTF_OUTPUT_VAR_FILE="/sys/firmware/efi/efivars/ByteCTFOut-$BYTECTF_OUTPUT_GUID"

#检查是否传入了命令行参数。如果没有提供参数,则输出用法说明并退出。
if [ "$1" == "" ]; then
echo "$0 <your input>"
exit 1
fi

#将第一个命令行参数赋值给 input 变量,并打印出来。
input=$1
echo "your input is $input"

#如果输出变量文件存在:
#将 input 赋值给 flag1。
#读取该文件的内容,使用 base64 编码并取前 24 个字符作为 flag2。
#输出格式化的字符串 ByteCTF{...},并删除输出文件。
#退出脚本。
if [ -f $BYTECTF_OUTPUT_VAR_FILE ]; then
flag1=$input
flag2=`cat $BYTECTF_OUTPUT_VAR_FILE | base64 | cut -c -24`
echo "ByteCTF{$flag1$flag2}"
chattr -i $BYTECTF_OUTPUT_VAR_FILE
rm -f $BYTECTF_OUTPUT_VAR_FILE
exit 0
fi

#如果输入变量文件存在,移除它(先通过 chattr -i 取消其不可变属性)。
if [ -f $BYTECTF_INPUT_VAR_FILE ]; then
chattr -i $BYTECTF_INPUT_VAR_FILE
rm -f $BYTECTF_INPUT_VAR_FILE
fi

#将输入数据(前面加上 4 字节的十六进制字符 \x07\x00\x00\x00)写入输入变量文件,使用 sudo 权限。
echo -en "\x07\x00\x00\x00$input" | sudo tee $BYTECTF_INPUT_VAR_FILE > /dev/null

#重启
echo "system will reboot in 10 seconds"
sh -c "sleep 10; reboot" &

根据分析可大致判断 flag 应该跟 UEFI 有关

UEFITool分析一下 bios.bin

搜索 ByteKit找到这个 PE32 镜像文件,右键 Extract Body导出

然后用 IDA 打开,发现有 OLLVM混淆,直接 D810 梭

ModuleEntryPoint 发现是一个简单的异或解密

IDA python解密一下,导出来发现是另一个 PE

1
2
3
4
5
6
key = bytes.fromhex('2CE83EE0BC1A0956B9D994')
enc = bytearray(ida_bytes.get_bytes(0x3A37, 0x6c4))
for i in range(len(enc)):
enc[i] ^= key[i%len(key)]
#print(enc.hex())
open('image.bin', 'wb').write(enc)

扔进 IDA 继续分析

一个简单的异或

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
enc = [0x4B, 0x27, 0x42, 0x55, 0x48, 0x6E, 0x41, 0x29, 0x1F, 0x5E, 0x04, 0x04, 0x6B, 0x3E, 0x57, 0x5F, 0x08, 0x07, 0x5F, 0x3A, 0x31, 0x17, 0x40, 0x30, 0x5F, 0x7A, 0x75, 0x67, 0x36, 0x36, 0x36, 0x36]
key = [0x62, 0x1, 0xb, 0x79, 0x2, 0x3, 0x74, 0x3, 0x7, 0x65, 0x4, 0xe, 0x64,
0x5, 0xd, 0x61, 0x6, 0xa, 0x6e, 0x7, 0xf, 0x63, 0x8, 0xc, 0x65, 0x9, 0xa, 0x0]
i = 0
while i != len(key) - 1:
v14 = key[i + 1]
v15 = v14 + key[i + 2]
while v15 > v14:
if v14 >=0 and v14 <= len(enc) - 1:
enc[v14] ^= key[i]
v14 += 1
i += 3
flag = ''.join([chr(x) for x in enc])
print(flag)
#KEY:By71d@nnc6_Wan77_y@0_zug6666

QEMU 运行 输两遍 KEY就行

1
2
3
4
5
qemu-system-x86_64 -L . -m 512M -nographic -monitor none -serial stdio -drive if=pflash,file=bios.bin,format=raw,readonly=off -net none -hda debian-12-nocloud-amd64-20240901-1857.qcow2
root
./getflag.sh KEY:By71d@nnc6_Wan77_y@0_zug6666
your input is KEY:By71d@nnc6_Wan77_y@0_zug6666
ByteCTF{KEY:By71d@nnc6_Wan77_y@0_zug6666BwAAAEsnQlVIbkEpH15qamZW}