├── .gitignore ├── LICENSE ├── README.md ├── build.sh ├── fs ├── etc │ └── passwd ├── flag └── init ├── launch.sh └── src ├── Makefile ├── hello_dev_char.c ├── hello_ioctl.c ├── hello_log.c ├── hello_proc_char.c └── make_root.c /.gitignore: -------------------------------------------------------------------------------- 1 | /src/*.mod.c 2 | /src/*.o 3 | /src/*.ko 4 | /src/*.cmd 5 | /src/*.mod 6 | /src/Module.symvers 7 | /src/modules.order 8 | /src/.tmp_versions 9 | /linux-* 10 | /busybox-* 11 | /initramfs.cpio.gz 12 | /bzImage 13 | /.gitignore 14 | /fs/*.ko 15 | /fs/linuxrc 16 | /fs/sbin 17 | /fs/usr 18 | /fs/bin 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018, Arizona Board of Regents 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pwn.college helper environment for kernel development and exploitation 2 | 3 | **NOTE: you don't need to interact with this repo in the course of interacting with pwn.college. The kernel challenges can be solved in the infrastructure; this is just here as a way to reproduce the infrastructure locally.** 4 | 5 | Pre-requistite: 6 | 7 | Building the kernel, busybox, and demo modules: 8 | 9 | ``` 10 | $ ./build.sh 11 | ``` 12 | 13 | Running the kernel: 14 | 15 | ``` 16 | $ ./launch.sh 17 | ``` 18 | 19 | All modules will be in `/`, ready to be `insmod`ed, and the host's home directory will be mounted as `/home/ctf` in the guest. 20 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | export KERNEL_VERSION=5.4 4 | export BUSYBOX_VERSION=1.32.0 5 | 6 | # 7 | # dependencies 8 | # 9 | echo "[+] Checking / installing dependencies..." 10 | sudo apt-get -q update 11 | sudo apt-get -q install -y bc bison flex libelf-dev cpio build-essential libssl-dev qemu-system-x86 12 | 13 | # 14 | # linux kernel 15 | # 16 | 17 | echo "[+] Downloading kernel..." 18 | wget -q -c https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/linux-$KERNEL_VERSION.tar.gz 19 | [ -e linux-$KERNEL_VERSION ] || tar xzf linux-$KERNEL_VERSION.tar.gz 20 | 21 | echo "[+] Building kernel..." 22 | make -C linux-$KERNEL_VERSION defconfig 23 | echo "CONFIG_NET_9P=y" >> linux-$KERNEL_VERSION/.config 24 | echo "CONFIG_NET_9P_DEBUG=n" >> linux-$KERNEL_VERSION/.config 25 | echo "CONFIG_9P_FS=y" >> linux-$KERNEL_VERSION/.config 26 | echo "CONFIG_9P_FS_POSIX_ACL=y" >> linux-$KERNEL_VERSION/.config 27 | echo "CONFIG_9P_FS_SECURITY=y" >> linux-$KERNEL_VERSION/.config 28 | echo "CONFIG_NET_9P_VIRTIO=y" >> linux-$KERNEL_VERSION/.config 29 | echo "CONFIG_VIRTIO_PCI=y" >> linux-$KERNEL_VERSION/.config 30 | echo "CONFIG_VIRTIO_BLK=y" >> linux-$KERNEL_VERSION/.config 31 | echo "CONFIG_VIRTIO_BLK_SCSI=y" >> linux-$KERNEL_VERSION/.config 32 | echo "CONFIG_VIRTIO_NET=y" >> linux-$KERNEL_VERSION/.config 33 | echo "CONFIG_VIRTIO_CONSOLE=y" >> linux-$KERNEL_VERSION/.config 34 | echo "CONFIG_HW_RANDOM_VIRTIO=y" >> linux-$KERNEL_VERSION/.config 35 | echo "CONFIG_DRM_VIRTIO_GPU=y" >> linux-$KERNEL_VERSION/.config 36 | echo "CONFIG_VIRTIO_PCI_LEGACY=y" >> linux-$KERNEL_VERSION/.config 37 | echo "CONFIG_VIRTIO_BALLOON=y" >> linux-$KERNEL_VERSION/.config 38 | echo "CONFIG_VIRTIO_INPUT=y" >> linux-$KERNEL_VERSION/.config 39 | echo "CONFIG_CRYPTO_DEV_VIRTIO=y" >> linux-$KERNEL_VERSION/.config 40 | echo "CONFIG_BALLOON_COMPACTION=y" >> linux-$KERNEL_VERSION/.config 41 | echo "CONFIG_PCI=y" >> linux-$KERNEL_VERSION/.config 42 | echo "CONFIG_PCI_HOST_GENERIC=y" >> linux-$KERNEL_VERSION/.config 43 | echo "CONFIG_GDB_SCRIPTS=y" >> linux-$KERNEL_VERSION/.config 44 | echo "CONFIG_DEBUG_INFO=y" >> linux-$KERNEL_VERSION/.config 45 | echo "CONFIG_DEBUG_INFO_REDUCED=n" >> linux-$KERNEL_VERSION/.config 46 | echo "CONFIG_DEBUG_INFO_SPLIT=n" >> linux-$KERNEL_VERSION/.config 47 | echo "CONFIG_DEBUG_FS=y" >> linux-$KERNEL_VERSION/.config 48 | echo "CONFIG_DEBUG_INFO_DWARF4=y" >> linux-$KERNEL_VERSION/.config 49 | echo "CONFIG_DEBUG_INFO_BTF=y" >> linux-$KERNEL_VERSION/.config 50 | echo "CONFIG_FRAME_POINTER=y" >> linux-$KERNEL_VERSION/.config 51 | 52 | sed -i 'N;s/WARN("missing symbol table");\n\t\treturn -1;/\n\t\treturn 0;\n\t\t\/\/ A missing symbol table is actually possible if its an empty .o file. This can happen for thunk_64.o./g' linux-$KERNEL_VERSION/tools/objtool/elf.c 53 | 54 | sed -i 's/unsigned long __force_order/\/\/ unsigned long __force_order/g' linux-$KERNEL_VERSION/arch/x86/boot/compressed/pgtable_64.c 55 | 56 | make -C linux-$KERNEL_VERSION -j16 bzImage 57 | 58 | # 59 | # Busybox 60 | # 61 | 62 | echo "[+] Downloading busybox..." 63 | wget -q -c https://busybox.net/downloads/busybox-$BUSYBOX_VERSION.tar.bz2 64 | [ -e busybox-$BUSYBOX_VERSION ] || tar xjf busybox-$BUSYBOX_VERSION.tar.bz2 65 | 66 | echo "[+] Building busybox..." 67 | make -C busybox-$BUSYBOX_VERSION defconfig 68 | sed -i 's/# CONFIG_STATIC is not set/CONFIG_STATIC=y/g' busybox-$BUSYBOX_VERSION/.config 69 | make -C busybox-$BUSYBOX_VERSION -j16 70 | make -C busybox-$BUSYBOX_VERSION install 71 | 72 | # 73 | # filesystem 74 | # 75 | 76 | echo "[+] Building filesystem..." 77 | cd fs 78 | mkdir -p bin sbin etc proc sys usr/bin usr/sbin root home/ctf 79 | cd .. 80 | cp -a busybox-$BUSYBOX_VERSION/_install/* fs 81 | 82 | # 83 | # modules 84 | # 85 | 86 | echo "[+] Building modules..." 87 | cd src 88 | make 89 | cd .. 90 | cp src/*.ko fs/ 91 | -------------------------------------------------------------------------------- /fs/etc/passwd: -------------------------------------------------------------------------------- 1 | root:x:0:0:root:/root:/bin/sh 2 | ctf:x:1000:1000:ctf:/home/ctf:/bin/sh 3 | root:x:0:0:root:/root:/bin/sh 4 | ctf:x:1000:1000:ctf:/home/ctf:/bin/sh 5 | -------------------------------------------------------------------------------- /fs/flag: -------------------------------------------------------------------------------- 1 | pwn_college{31337} 2 | -------------------------------------------------------------------------------- /fs/init: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | mount -t proc none /proc 4 | mount -t sysfs none /sys 5 | mount -t 9p -o trans=virtio,version=9p2000.L,nosuid hostshare /home/ctf 6 | #for f in $(ls *.ko); do 7 | # insmod $f 8 | #done 9 | sysctl -w kernel.perf_event_paranoid=1 10 | 11 | cat < ../initramfs.cpio.gz 8 | popd 9 | 10 | # 11 | # launch 12 | # 13 | /usr/bin/qemu-system-x86_64 \ 14 | -kernel linux-5.4/arch/x86/boot/bzImage \ 15 | -initrd $PWD/initramfs.cpio.gz \ 16 | -fsdev local,security_model=passthrough,id=fsdev0,path=$HOME \ 17 | -device virtio-9p-pci,id=fs0,fsdev=fsdev0,mount_tag=hostshare \ 18 | -nographic \ 19 | -monitor none \ 20 | -s \ 21 | -append "console=ttyS0 nokaslr" 22 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | # add more modules here! 2 | obj-m = hello_dev_char.o hello_ioctl.o hello_log.o hello_proc_char.o make_root.o 3 | KERNEL_VERSION=5.4 4 | 5 | all: 6 | echo $(OBJECTS) 7 | make -C ../linux-$(KERNEL_VERSION) M=$(PWD) modules 8 | 9 | clean: 10 | make -C ../linux-$(KERNEL_VERSION) M=$(PWD) clean 11 | -------------------------------------------------------------------------------- /src/hello_dev_char.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | MODULE_LICENSE("GPL"); 7 | 8 | static int major_number; 9 | 10 | static int device_open(struct inode *inode, struct file *filp) 11 | { 12 | printk(KERN_ALERT "Device opened."); 13 | return 0; 14 | } 15 | 16 | static int device_release(struct inode *inode, struct file *filp) 17 | { 18 | printk(KERN_ALERT "Device closed."); 19 | return 0; 20 | } 21 | 22 | static ssize_t device_read(struct file *filp, char *buffer, size_t length, loff_t *offset) 23 | { 24 | char *msg = "Hello pwn.college!\n"; 25 | return copy_to_user(buffer, msg, strlen(msg)) ? -EFAULT : 0; 26 | } 27 | 28 | static ssize_t device_write(struct file *filp, const char *buf, size_t len, loff_t *off) 29 | { 30 | printk(KERN_ALERT "Sorry, this operation isn't supported.\n"); 31 | return -EINVAL; 32 | } 33 | 34 | static struct file_operations fops = { 35 | .read = device_read, 36 | .write = device_write, 37 | .open = device_open, 38 | .release = device_release 39 | }; 40 | 41 | int init_module(void) 42 | { 43 | major_number = register_chrdev(0, "pwn-college-char", &fops); 44 | 45 | if (major_number < 0) { 46 | printk(KERN_ALERT "Registering char device failed with %d\n", major_number); 47 | return major_number; 48 | } 49 | 50 | printk(KERN_INFO "I was assigned major number %d.\n", major_number); 51 | printk(KERN_INFO "Create device with: 'mknod /dev/pwn-college-char c %d 0'.\n", major_number); 52 | return 0; 53 | } 54 | 55 | void cleanup_module(void) 56 | { 57 | unregister_chrdev(major_number, "pwn-college-char"); 58 | } 59 | 60 | -------------------------------------------------------------------------------- /src/hello_ioctl.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define PWN_GET _IO('p', 1) 10 | #define PWN_SET _IO('p', 2) 11 | 12 | MODULE_LICENSE("GPL"); 13 | 14 | char flag[128]; 15 | static int device_open(struct inode *inode, struct file *filp) 16 | { 17 | printk(KERN_ALERT "Device opened.\n"); 18 | return 0; 19 | } 20 | 21 | static int device_release(struct inode *inode, struct file *filp) 22 | { 23 | printk(KERN_ALERT "Device closed.\n"); 24 | return 0; 25 | } 26 | 27 | static ssize_t device_read(struct file *filp, char *buffer, size_t length, loff_t *offset) 28 | { 29 | return -EINVAL; 30 | } 31 | 32 | static ssize_t device_write(struct file *filp, const char *buf, size_t len, loff_t *off) 33 | { 34 | return -EINVAL; 35 | } 36 | 37 | char message[16]; 38 | static long device_ioctl(struct file *filp, unsigned int ioctl_num, unsigned long ioctl_param) 39 | { 40 | int is_copy_invalid = 0; 41 | printk(KERN_ALERT "Got ioctl argument %#x!", ioctl_num); 42 | if (ioctl_num == PWN_GET && strcmp(message, "PASSWORD") == 0) { 43 | printk(KERN_ALERT "Writing to userspace!\n"); 44 | is_copy_invalid = copy_to_user((char *)ioctl_param, flag, 128); 45 | } else if (ioctl_num == PWN_SET) { 46 | printk(KERN_ALERT "Reading from userspace!\n"); 47 | is_copy_invalid = copy_from_user(message, (char *)ioctl_param, 16); 48 | } 49 | 50 | if (is_copy_invalid) 51 | return -EFAULT; 52 | return 0; 53 | } 54 | 55 | static struct file_operations fops = { 56 | .read = device_read, 57 | .write = device_write, 58 | .unlocked_ioctl = device_ioctl, 59 | .open = device_open, 60 | .release = device_release 61 | }; 62 | 63 | struct proc_dir_entry *proc_entry = NULL; 64 | 65 | int init_module(void) 66 | { 67 | // read in flag file 68 | loff_t offset = 0; 69 | struct file *flag_fd; 70 | flag_fd = filp_open("/flag", O_RDONLY, 0); 71 | kernel_read(flag_fd, flag, 128, &offset); 72 | filp_close(flag_fd, NULL); 73 | 74 | printk(KERN_ALERT "ioctl address: %#lx\b", (unsigned long)device_ioctl); 75 | printk(KERN_ALERT "PWN_GET value: %#x\b", PWN_GET); 76 | printk(KERN_ALERT "PWN_SET value: %#x\b", PWN_SET); 77 | proc_entry = proc_create("pwn-college-ioctl", 0666, NULL, &fops); 78 | return 0; 79 | } 80 | 81 | void cleanup_module(void) 82 | { 83 | if (proc_entry) proc_remove(proc_entry); 84 | } 85 | 86 | -------------------------------------------------------------------------------- /src/hello_log.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | MODULE_LICENSE("GPL"); 5 | 6 | int init_module(void) 7 | { 8 | printk(KERN_INFO "Hello pwn.college!\n"); 9 | return 0; 10 | } 11 | 12 | void cleanup_module(void) 13 | { 14 | printk(KERN_INFO "Goodbye pwn.college!\n"); 15 | } 16 | -------------------------------------------------------------------------------- /src/hello_proc_char.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | MODULE_LICENSE("GPL"); 8 | 9 | static int device_open(struct inode *inode, struct file *filp) 10 | { 11 | printk(KERN_ALERT "Device opened."); 12 | return 0; 13 | } 14 | 15 | static int device_release(struct inode *inode, struct file *filp) 16 | { 17 | printk(KERN_ALERT "Device closed."); 18 | return 0; 19 | } 20 | 21 | static ssize_t device_read(struct file *filp, char *buffer, size_t length, loff_t *offset) 22 | { 23 | char *msg = "Hello pwn-college!\n"; 24 | return copy_to_user(buffer, msg, strlen(msg)) ? -EFAULT : 0; 25 | } 26 | 27 | static ssize_t device_write(struct file *filp, const char *buf, size_t len, loff_t *off) 28 | { 29 | printk(KERN_ALERT "Sorry, this operation isn't supported.\n"); 30 | return -EINVAL; 31 | } 32 | 33 | static struct file_operations fops = { 34 | .read = device_read, 35 | .write = device_write, 36 | .open = device_open, 37 | .release = device_release 38 | }; 39 | 40 | struct proc_dir_entry *proc_entry = NULL; 41 | 42 | int init_module(void) 43 | { 44 | proc_entry = proc_create("pwn-college-char", 0666, NULL, &fops); 45 | printk(KERN_ALERT "/proc/pwn-college-char created!"); 46 | return 0; 47 | } 48 | 49 | void cleanup_module(void) 50 | { 51 | if (proc_entry) proc_remove(proc_entry); 52 | printk(KERN_ALERT "/proc/pwn-college-char removed!"); 53 | } 54 | 55 | -------------------------------------------------------------------------------- /src/make_root.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define PWN _IO('p', 1) 9 | 10 | MODULE_LICENSE("GPL"); 11 | 12 | static int device_open(struct inode *inode, struct file *filp) 13 | { 14 | printk(KERN_ALERT "Device opened."); 15 | return 0; 16 | } 17 | 18 | static int device_release(struct inode *inode, struct file *filp) 19 | { 20 | printk(KERN_ALERT "Device closed."); 21 | return 0; 22 | } 23 | 24 | static ssize_t device_read(struct file *filp, char *buffer, size_t length, loff_t *offset) 25 | { 26 | return -EINVAL; 27 | } 28 | 29 | static ssize_t device_write(struct file *filp, const char *buf, size_t len, loff_t *off) 30 | { 31 | return -EINVAL; 32 | } 33 | 34 | static long device_ioctl(struct file *filp, unsigned int ioctl_num, unsigned long ioctl_param) 35 | { 36 | printk(KERN_ALERT "Got ioctl argument %d!", ioctl_num); 37 | if (ioctl_num == PWN) 38 | { 39 | if (ioctl_param == 0x13371337) 40 | { 41 | printk(KERN_ALERT "Granting root access!"); 42 | commit_creds(prepare_kernel_cred(NULL)); 43 | } 44 | if (ioctl_param == 0x31337) 45 | { 46 | printk(KERN_ALERT "Escaping seccomp!"); 47 | printk(KERN_ALERT "FLAGS BEFORE: %lx", current->thread_info.flags); 48 | current->thread_info.flags &= ~_TIF_SECCOMP; 49 | printk(KERN_ALERT "FLAGS AFTER: %lx", current->thread_info.flags); 50 | } 51 | } 52 | return 0; 53 | } 54 | 55 | static struct file_operations fops = { 56 | .read = device_read, 57 | .write = device_write, 58 | .unlocked_ioctl = device_ioctl, 59 | .open = device_open, 60 | .release = device_release 61 | }; 62 | 63 | struct proc_dir_entry *proc_entry = NULL; 64 | 65 | int init_module(void) 66 | { 67 | proc_entry = proc_create("pwn-college-root", 0666, NULL, &fops); 68 | return 0; 69 | } 70 | 71 | void cleanup_module(void) 72 | { 73 | if (proc_entry) proc_remove(proc_entry); 74 | } 75 | 76 | --------------------------------------------------------------------------------