首页
社区
课程
招聘
阿里云CTF2024暴力ENOTYOURWORLD题解
发表于: 2024-4-1 01:19 9255

阿里云CTF2024暴力ENOTYOURWORLD题解

2024-4-1 01:19
9255

CTF authors, please don't put PhD level math in reverse engineering challenges. thank you.

(好吧其实不是PhD level math,逃~)

题目附件:b3e67cd8a0f9ccc3bcfdf2deb8b0166dc6fb5fb745584426fb152e36c7abe56e

分析附件,不难发现为脚本文件。脚本文件执行Base85解码和LZMA解压释放文件。编写Python代码提取如下(节约篇幅,有删减)。

常规步骤,检查字符串。执行命令如下。

得到结果如下。

自然需要部署工具环境。执行命令如下。

根据脚本文件,不难给出可用的链接命令如下。不妨假设所有链接定义符号为零。

常规步骤,检查反汇编。执行命令如下。

不难发现直接提示错误,无额外逻辑。结合链接定义符号,怀疑重定位表覆写。检查重定位。执行命令如下。

关注到.rela.text.这一部分内容过长,不符合常规。不难将这一部分导出为CSV表格文件。

不妨使用Frida计算文件偏移和有效地址之间的关系(JavaScript脚本略,可通过内存布局得到)。

得到文件偏移和有效地址偏差mem_delta = 0x01FFFBB5。此外另从ELF结构读取文件偏移reloca_foff = 0x0000045B和大小reloca_size = 0x00245940

LARCH重定位涉及基于栈的虚拟机。对于LARCH虚拟机,不用慌张,相较于传统VM逆向题目,各个操作功能及其参数是已知的。不妨转换为C代码并使用编译器优化。编写Python代码转换如下。需要注意的是,操作数可能被覆写,因此需要特殊处理。

执行脚本,生成C代码头文件。不妨配合以下C代码源文件使用。

不妨使用GCC编译并启用O2优化。命令如下。

此时我们已经将虚拟代码还原。使用常规工具反编译。关注到如下片段。

不妨发现为核心比较。

进一步观察,发现其中4个校验为相邻两部分的拼接经过运算的结果。具体拼接方式如下。

运算方式如下(节约篇幅,有删减)。大致为数域运算。

另外16个校验为每个部分两次经过运算的结果。运算方式大致为杂凑算法。

让我们来总结一下手里的东西。

不妨猜测:按照正常的逻辑,数域运算应当是可解的。我们应该得到每个flag_v?的一半部分(4字节,32位),再暴力枚举剩余一半部分(4字节,32位)。

但是……参见“0x00 前言”。看不懂数学怎么办?z3?(数域运算大概率是非对称的,约束求解大概率是困难的)暴力!

由于直接暴力枚举杂凑算法开销过大,不妨枚举数域运算。然后再按照猜测的剩余逻辑求解。相信我,经过测试,不做优化的情况下单核单线程的CCF老年机也只需要万年就能全部枚举一遍。优化代码使用高性能计算机并采用并行计算,效率大幅提升。

但是为了节约用电,请不要这么做。不仅如此,这种方式可能无法让你在有限时间的比赛中提交答案。(狗头)

import base64
import lzma
 
O = b"..."
with open("object", "wb") as f:
    f.write(lzma.decompress(base64.a85decode(O)))
import base64
import lzma
 
O = b"..."
with open("object", "wb") as f:
    f.write(lzma.decompress(base64.a85decode(O)))
strings object | grep GCC
strings object | grep GCC
GCC: (LoongArch GNU toolchain rc1.0) 8.3.0
GCC: (LoongArch GNU toolchain rc1.0) 8.3.0
wget http://ftp.loongnix.cn/toolchain/gcc/release/loongarch/gcc8/toolchain-loongarch64-linux-gnu-cross-830-rc1.0-2022-04-22.tar.xz
tar -xf toolchain-loongarch64-linux-gnu-cross-830-rc1.0-2022-04-22.tar.xz
pushd toolchain-loongarch64-linux-gnu-cross-830-rc1.0-2022-04-22
export TC_HOME=`pwd`
pushd bin
export PATH=$PATH:`pwd`
popd
popd
wget http://ftp.loongnix.cn/toolchain/gcc/release/loongarch/gcc8/toolchain-loongarch64-linux-gnu-cross-830-rc1.0-2022-04-22.tar.xz
tar -xf toolchain-loongarch64-linux-gnu-cross-830-rc1.0-2022-04-22.tar.xz
pushd toolchain-loongarch64-linux-gnu-cross-830-rc1.0-2022-04-22
export TC_HOME=`pwd`
pushd bin
export PATH=$PATH:`pwd`
popd
popd
$TC_HOME/loongarch64-linux-gnu/bin/ld -plugin $TC_HOME/libexec/gcc/loongarch64-linux-gnu/8.3.0/liblto_plugin.so -plugin-opt=$TC_HOME/libexec/gcc/loongarch64-linux-gnu/8.3.0/lto-wrapper -plugin-opt=-fresolution=~/resolution.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_eh -plugin-opt=-pass-through=-lc --sysroot=$TC_HOME/sysroot -m elf64loongarch -static -o ~/output $TC_HOME/sysroot/lib64/crt1.o $TC_HOME/sysroot/lib64/crti.o $TC_HOME/lib/gcc/loongarch64-linux-gnu/8.3.0/crtbeginT.o -L$TC_HOME/lib/gcc/loongarch64-linux-gnu/8.3.0 -L$TC_HOME/lib/gcc -L$TC_HOME/sysroot/lib64 -L$TC_HOME/loongarch64-linux-gnu/lib -L$TC_HOME/sysroot/lib ~/object --defsym=v0=0 --defsym=v1=0 --defsym=v2=0 --defsym=v3=0 --defsym=v4=0 --defsym=v5=0 --defsym=v6=0 --defsym=v7=0 --start-group -lgcc -lgcc_eh -lc --end-group $TC_HOME/lib/gcc/loongarch64-linux-gnu/8.3.0/crtend.o $TC_HOME/sysroot/lib64/crtn.o
$TC_HOME/loongarch64-linux-gnu/bin/ld -plugin $TC_HOME/libexec/gcc/loongarch64-linux-gnu/8.3.0/liblto_plugin.so -plugin-opt=$TC_HOME/libexec/gcc/loongarch64-linux-gnu/8.3.0/lto-wrapper -plugin-opt=-fresolution=~/resolution.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_eh -plugin-opt=-pass-through=-lc --sysroot=$TC_HOME/sysroot -m elf64loongarch -static -o ~/output $TC_HOME/sysroot/lib64/crt1.o $TC_HOME/sysroot/lib64/crti.o $TC_HOME/lib/gcc/loongarch64-linux-gnu/8.3.0/crtbeginT.o -L$TC_HOME/lib/gcc/loongarch64-linux-gnu/8.3.0 -L$TC_HOME/lib/gcc -L$TC_HOME/sysroot/lib64 -L$TC_HOME/loongarch64-linux-gnu/lib -L$TC_HOME/sysroot/lib ~/object --defsym=v0=0 --defsym=v1=0 --defsym=v2=0 --defsym=v3=0 --defsym=v4=0 --defsym=v5=0 --defsym=v6=0 --defsym=v7=0 --start-group -lgcc -lgcc_eh -lc --end-group $TC_HOME/lib/gcc/loongarch64-linux-gnu/8.3.0/crtend.o $TC_HOME/sysroot/lib64/crtn.o
loongarch64-linux-gnu-objdump --disassemble object
loongarch64-linux-gnu-objdump --disassemble object
loongarch64-linux-gnu-objdump --reloc object
loongarch64-linux-gnu-objdump --reloc object
frida -f $TC_HOME/loongarch64-linux-gnu/bin/ld -- -plugin $TC_HOME/libexec/gcc/loongarch64-linux-gnu/8.3.0/liblto_plugin.so -plugin-opt=$TC_HOME/libexec/gcc/loongarch64-linux-gnu/8.3.0/lto-wrapper -plugin-opt=-fresolution=~/resolution.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_eh -plugin-opt=-pass-through=-lc --sysroot=$TC_HOME/sysroot -m elf64loongarch -static -o ~/output $TC_HOME/sysroot/lib64/crt1.o $TC_HOME/sysroot/lib64/crti.o $TC_HOME/lib/gcc/loongarch64-linux-gnu/8.3.0/crtbeginT.o -L$TC_HOME/lib/gcc/loongarch64-linux-gnu/8.3.0 -L$TC_HOME/lib/gcc -L$TC_HOME/sysroot/lib64 -L$TC_HOME/loongarch64-linux-gnu/lib -L$TC_HOME/sysroot/lib ~/object --defsym=v0=0 --defsym=v1=0 --defsym=v2=0 --defsym=v3=0 --defsym=v4=0 --defsym=v5=0 --defsym=v6=0 --defsym=v7=0 --start-group -lgcc -lgcc_eh -lc --end-group $TC_HOME/lib/gcc/loongarch64-linux-gnu/8.3.0/crtend.o $TC_HOME/sysroot/lib64/crtn.o
frida -f $TC_HOME/loongarch64-linux-gnu/bin/ld -- -plugin $TC_HOME/libexec/gcc/loongarch64-linux-gnu/8.3.0/liblto_plugin.so -plugin-opt=$TC_HOME/libexec/gcc/loongarch64-linux-gnu/8.3.0/lto-wrapper -plugin-opt=-fresolution=~/resolution.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_eh -plugin-opt=-pass-through=-lc --sysroot=$TC_HOME/sysroot -m elf64loongarch -static -o ~/output $TC_HOME/sysroot/lib64/crt1.o $TC_HOME/sysroot/lib64/crti.o $TC_HOME/lib/gcc/loongarch64-linux-gnu/8.3.0/crtbeginT.o -L$TC_HOME/lib/gcc/loongarch64-linux-gnu/8.3.0 -L$TC_HOME/lib/gcc -L$TC_HOME/sysroot/lib64 -L$TC_HOME/loongarch64-linux-gnu/lib -L$TC_HOME/sysroot/lib ~/object --defsym=v0=0 --defsym=v1=0 --defsym=v2=0 --defsym=v3=0 --defsym=v4=0 --defsym=v5=0 --defsym=v6=0 --defsym=v7=0 --start-group -lgcc -lgcc_eh -lc --end-group $TC_HOME/lib/gcc/loongarch64-linux-gnu/8.3.0/crtend.o $TC_HOME/sysroot/lib64/crtn.o
import csv
 
source = []
 
mem_delta = 0x01FFFBB5
reloca_foff = 0x0000045B
reloca_size = 0x00245940
reloca_start = mem_delta + reloca_foff
reloca_end = reloca_start + reloca_size
value_addrs = []
addend_writable_addrs = []
for i in range(reloca_start, reloca_end, 24):
    value_addrs.append(i + 16)
    addend_writable_addrs.append(i + 16)
    addend_writable_addrs.append(i + 20)
 
with open("reloc.csv") as f:
    c = csv.reader(f)
    c = list(c)
 
assert len(c) == len(value_addrs)
 
modified_value_addrs = []
 
SP = 0
 
for (OFFSET, TYPE, VALUE), VALUE_ADDR in zip(c, value_addrs):
    if TYPE in ["R_LARCH_SOP_PUSH_PCREL", "R_LARCH_SOP_PUSH_PLT_PCREL"]:
        SP += 1
    elif TYPE == "R_LARCH_SOP_PUSH_ABSOLUTE":
        if VALUE.startswith("*ABS*"):
            t1 = VALUE_ADDR
            t2 = VALUE_ADDR + 4
            lpart = t1 in modified_value_addrs
            rpart = t2 in modified_value_addrs
            if lpart or rpart:
                assert lpart and rpart
                source.append(
                    f"STK_U64_{SP} = ((uint64_t)LOC_U32_{t2:08X} << 32) | LOC_U32_{t1:08X};"
                )
                SP += 1
            else:
                if VALUE == "*ABS*":
                    source.append(f"STK_U64_{SP} = 0;")
                    SP += 1
                else:
                    source.append(f"STK_U64_{SP} = {VALUE[5:]};")
                    SP += 1
        elif VALUE in ["v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7"]:
            source.append(f"STK_U64_{SP} = flag_{VALUE};")
            SP += 1
        else:
            assert False, f"Unknown VALUE {VALUE}"
    elif TYPE == "R_LARCH_SOP_SR":
        SP -= 1
        opr2 = f"STK_U64_{SP}"
        SP -= 1
        opr1 = f"STK_U64_{SP}"
        source.append(f"STK_U64_{SP} = {opr1} >> {opr2};")
        SP += 1
    elif TYPE == "R_LARCH_SOP_SL":
        SP -= 1
        opr2 = f"STK_U64_{SP}"
        SP -= 1
        opr1 = f"STK_U64_{SP}"
        source.append(f"STK_U64_{SP} = {opr1} << {opr2};")
        SP += 1
    elif TYPE == "R_LARCH_SOP_SUB":
        SP -= 1
        opr2 = f"STK_U64_{SP}"
        SP -= 1
        opr1 = f"STK_U64_{SP}"
        source.append(f"STK_U64_{SP} = {opr1} - {opr2};")
        SP += 1
    elif TYPE == "R_LARCH_SOP_PUSH_DUP":
        SP -= 1
        opr1 = f"STK_U64_{SP}"
        source.append(f"STK_U64_{SP} = {opr1};")
        SP += 1
        source.append(f"STK_U64_{SP} = {opr1};")
        SP += 1
    elif TYPE == "R_LARCH_SOP_AND":
        SP -= 1
        opr2 = f"STK_U64_{SP}"
        SP -= 1
        opr1 = f"STK_U64_{SP}"
        source.append(f"STK_U64_{SP} = {opr1} & {opr2};")
        SP += 1
    elif TYPE == "R_LARCH_SOP_ADD":
        SP -= 1
        opr2 = f"STK_U64_{SP}"
        SP -= 1
        opr1 = f"STK_U64_{SP}"
        source.append(f"STK_U64_{SP} = {opr1} + {opr2};")
        SP += 1
    elif TYPE == "R_LARCH_SOP_IF_ELSE":
        SP -= 1
        opr3 = f"STK_U64_{SP}"
        SP -= 1
        opr2 = f"STK_U64_{SP}"
        SP -= 1
        opr1 = f"STK_U64_{SP}"
        source.append(f"STK_U64_{SP} = {opr1} ? {opr2} : {opr3};")
        SP += 1
    elif TYPE == "R_LARCH_SOP_NOT":
        SP -= 1
        v = f"STK_U64_{SP}"
        source.append(f"STK_U64_{SP} = !{v};")
        SP += 1
    elif TYPE == "R_LARCH_SOP_ASSERT":
        SP -= 1
        v = f"STK_U64_{SP}"
        source.append(f"assert({v});")
    elif TYPE in [
        "R_LARCH_SOP_POP_32_S_5_20",
        "R_LARCH_SOP_POP_32_S_10_12",
        "R_LARCH_SOP_POP_32_S_0_10_10_16_S2",
    ]:
        SP -= 1
    elif TYPE == "R_LARCH_SOP_POP_32_U":
        t = int(OFFSET, 16)
        if reloca_start <= t < reloca_end:
            assert t in addend_writable_addrs
            modified_value_addrs.append(t)
            SP -= 1
            v = f"STK_U64_{SP}"
            source.append(f"LOC_U32_{t:08X} = {v} & 0xffffffff;")
        else:
            SP -= 1
    elif TYPE == "R_LARCH_ADD32":
        pass
    else:
        assert False, f"Unknown TYPE {TYPE}"
 
for i in range(4):
    print(f"uint64_t STK_U64_{i} = 0;")
print()
for i in modified_value_addrs:
    print(f"uint32_t LOC_U32_{i:08X};")
print()
for i in source:
    print(i)
import csv
 
source = []
 
mem_delta = 0x01FFFBB5
reloca_foff = 0x0000045B
reloca_size = 0x00245940
reloca_start = mem_delta + reloca_foff
reloca_end = reloca_start + reloca_size
value_addrs = []
addend_writable_addrs = []
for i in range(reloca_start, reloca_end, 24):
    value_addrs.append(i + 16)
    addend_writable_addrs.append(i + 16)
    addend_writable_addrs.append(i + 20)
 
with open("reloc.csv") as f:
    c = csv.reader(f)
    c = list(c)
 
assert len(c) == len(value_addrs)
 
modified_value_addrs = []
 
SP = 0
 
for (OFFSET, TYPE, VALUE), VALUE_ADDR in zip(c, value_addrs):
    if TYPE in ["R_LARCH_SOP_PUSH_PCREL", "R_LARCH_SOP_PUSH_PLT_PCREL"]:
        SP += 1
    elif TYPE == "R_LARCH_SOP_PUSH_ABSOLUTE":
        if VALUE.startswith("*ABS*"):
            t1 = VALUE_ADDR
            t2 = VALUE_ADDR + 4
            lpart = t1 in modified_value_addrs
            rpart = t2 in modified_value_addrs
            if lpart or rpart:
                assert lpart and rpart
                source.append(
                    f"STK_U64_{SP} = ((uint64_t)LOC_U32_{t2:08X} << 32) | LOC_U32_{t1:08X};"
                )
                SP += 1
            else:
                if VALUE == "*ABS*":
                    source.append(f"STK_U64_{SP} = 0;")
                    SP += 1
                else:
                    source.append(f"STK_U64_{SP} = {VALUE[5:]};")
                    SP += 1
        elif VALUE in ["v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7"]:
            source.append(f"STK_U64_{SP} = flag_{VALUE};")
            SP += 1
        else:
            assert False, f"Unknown VALUE {VALUE}"
    elif TYPE == "R_LARCH_SOP_SR":
        SP -= 1
        opr2 = f"STK_U64_{SP}"
        SP -= 1
        opr1 = f"STK_U64_{SP}"
        source.append(f"STK_U64_{SP} = {opr1} >> {opr2};")
        SP += 1
    elif TYPE == "R_LARCH_SOP_SL":
        SP -= 1
        opr2 = f"STK_U64_{SP}"
        SP -= 1
        opr1 = f"STK_U64_{SP}"
        source.append(f"STK_U64_{SP} = {opr1} << {opr2};")
        SP += 1
    elif TYPE == "R_LARCH_SOP_SUB":
        SP -= 1
        opr2 = f"STK_U64_{SP}"
        SP -= 1
        opr1 = f"STK_U64_{SP}"
        source.append(f"STK_U64_{SP} = {opr1} - {opr2};")
        SP += 1
    elif TYPE == "R_LARCH_SOP_PUSH_DUP":
        SP -= 1
        opr1 = f"STK_U64_{SP}"
        source.append(f"STK_U64_{SP} = {opr1};")
        SP += 1
        source.append(f"STK_U64_{SP} = {opr1};")
        SP += 1
    elif TYPE == "R_LARCH_SOP_AND":
        SP -= 1
        opr2 = f"STK_U64_{SP}"
        SP -= 1
        opr1 = f"STK_U64_{SP}"
        source.append(f"STK_U64_{SP} = {opr1} & {opr2};")
        SP += 1
    elif TYPE == "R_LARCH_SOP_ADD":
        SP -= 1
        opr2 = f"STK_U64_{SP}"
        SP -= 1
        opr1 = f"STK_U64_{SP}"

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 1
支持
分享
最新回复 (2)
雪    币: 232
活跃值: (350)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
太帅了
2024-4-1 11:17
0
雪    币: 3059
活跃值: (30876)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
感谢分享
2024-4-1 14:49
1
游客
登录 | 注册 方可回帖
返回
//