diff options
Diffstat (limited to 'ramfs')
| -rwxr-xr-x | ramfs/bin/busybox | bin | 0 -> 1005608 bytes | |||
| -rwxr-xr-x | ramfs/bin/ptree | bin | 0 -> 58940 bytes | |||
| l--------- | ramfs/bin/sh | 1 | ||||
| -rw-r--r-- | ramfs/home/.profile | 5 | ||||
| -rw-r--r-- | ramfs/home/README | 21 | ||||
| -rwxr-xr-x | ramfs/init | 29 | ||||
| -rwxr-xr-x | ramfs/sbin/dhcpassign.sh | 34 | ||||
| -rw-r--r-- | ramfs/src/linux-3.5.4-ptree.patch | 115 | ||||
| -rw-r--r-- | ramfs/src/ptree.c | 79 |
9 files changed, 284 insertions, 0 deletions
diff --git a/ramfs/bin/busybox b/ramfs/bin/busybox Binary files differnew file mode 100755 index 0000000..e51a71e --- /dev/null +++ b/ramfs/bin/busybox diff --git a/ramfs/bin/ptree b/ramfs/bin/ptree Binary files differnew file mode 100755 index 0000000..888c590 --- /dev/null +++ b/ramfs/bin/ptree diff --git a/ramfs/bin/sh b/ramfs/bin/sh new file mode 120000 index 0000000..c3fa810 --- /dev/null +++ b/ramfs/bin/sh @@ -0,0 +1 @@ +busybox
\ No newline at end of file diff --git a/ramfs/home/.profile b/ramfs/home/.profile new file mode 100644 index 0000000..7a6e604 --- /dev/null +++ b/ramfs/home/.profile @@ -0,0 +1,5 @@ +if [ "$(id -u)" == "0" ] ; then + PS1='\[\033[01;31m\]hackme\[\033[01;34m\] \W #\[\033[00m\] ' +else + PS1='\[\033[01;32m\]not-yet-root@hackme\[\033[01;34m\] \w \$\[\033[00m\] ' +fi diff --git a/ramfs/home/README b/ramfs/home/README new file mode 100644 index 0000000..48f8c16 --- /dev/null +++ b/ramfs/home/README @@ -0,0 +1,21 @@ +=== Welcome to the ZX2C4 Kernel Challenge === + by Jason A. Donenfeld <Jason@zx2c4.com> + + +Pwn the kernel and pop a root shell! + +Source is available inside /src. + + +eth0 receives an IP address via dhcp. It may be helpful to do +development on the host and send binaries to the VM: + +On the VM: + /home $ while true; do nc -l -p 1234 > a; chmod +x a; ./a; done + +On the host: + $ gcc -static -o a vuln.c && nc 192.168.56.102 1234 < a + + +More information about this project is available at: + <http://git.zx2c4.com/kernel-pwn-challenge/about/> diff --git a/ramfs/init b/ramfs/init new file mode 100755 index 0000000..cb64951 --- /dev/null +++ b/ramfs/init @@ -0,0 +1,29 @@ +#!/bin/sh + +mkdir -p /dev /proc /sys /etc +[ ! -e /dev/console ] && mknod /dev/console c 5 1 +[ ! -e /dev/null ] && mknod /dev/null c 1 3 +[ ! -e /dev/tty ] && mknod /dev/tty c 5 0 +[ ! -e /dev/urandom ] && mknod /dev/urandom c 1 9 +[ ! -e /dev/random ] && mknod /dev/random c 1 8 +[ ! -e /dev/zero ] && mknod /dev/zero c 1 5 + +mount -t sysfs sysfs /sys +mount -t proc proc /proc +mount -t devtmpfs devtmpfs /dev +echo 0 > /proc/sys/kernel/printk +/bin/busybox --install -s +ifconfig eth0 up +udhcpc -b -i eth0 -s /sbin/dhcpassign.sh +rm /init +clear + +export HOME=/home +export ENV=$HOME/.profile +chown -R 1000:1000 $HOME +cd $HOME +while true; do + cat README + setsid sh -c 'exec sh --login -c "exec chpst -u 1000:1000 /bin/sh" </dev/tty1 >/dev/tty1 2>&1' + clear +done diff --git a/ramfs/sbin/dhcpassign.sh b/ramfs/sbin/dhcpassign.sh new file mode 100755 index 0000000..347a37c --- /dev/null +++ b/ramfs/sbin/dhcpassign.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +RESOLV_CONF="/etc/resolv.conf" +[ -n "$broadcast" ] && BROADCAST="broadcast $broadcast" +[ -n "$subnet" ] && NETMASK="netmask $subnet" + +case "$1" in + deconfig) + /sbin/ifconfig $interface 0.0.0.0 + ;; + + renew|bound) + /sbin/ifconfig $interface $ip $BROADCAST $NETMASK + + if [ -n "$router" ] ; then + echo "deleting routers" + while route del default gw 0.0.0.0 dev $interface ; do + : + done + + for i in $router ; do + route add default gw $i dev $interface + done + fi + + echo -n > $RESOLV_CONF + [ -n "$domain" ] && echo search $domain >> $RESOLV_CONF + for i in $dns ; do + echo nameserver $i >> $RESOLV_CONF + done + ;; +esac + +exit 0 diff --git a/ramfs/src/linux-3.5.4-ptree.patch b/ramfs/src/linux-3.5.4-ptree.patch new file mode 100644 index 0000000..ee6182d --- /dev/null +++ b/ramfs/src/linux-3.5.4-ptree.patch @@ -0,0 +1,115 @@ +diff -ru linux-3.5.4/arch/x86/syscalls/syscall_64.tbl linux-3.5.4-compile/arch/x86/syscalls/syscall_64.tbl +--- linux-3.5.4/arch/x86/syscalls/syscall_64.tbl 2012-09-15 00:28:08.000000000 +0200 ++++ linux-3.5.4-compile/arch/x86/syscalls/syscall_64.tbl 2012-09-28 04:45:49.232275782 +0200 +@@ -319,6 +319,7 @@ + 310 64 process_vm_readv sys_process_vm_readv + 311 64 process_vm_writev sys_process_vm_writev + 312 common kcmp sys_kcmp ++313 common ptree sys_ptree + + # + # x32-specific system call numbers start at 512 to avoid cache impact +diff -ru linux-3.5.4/include/linux/sched.h linux-3.5.4-compile/include/linux/sched.h +--- linux-3.5.4/include/linux/sched.h 2012-09-15 00:28:08.000000000 +0200 ++++ linux-3.5.4-compile/include/linux/sched.h 2012-09-28 04:45:49.273275264 +0200 +@@ -465,6 +465,16 @@ + u32 incr_error; + }; + ++struct prinfo { ++ long state; ++ pid_t pid; ++ pid_t parent_pid; ++ pid_t first_child_pid; ++ pid_t next_sibling_pid; ++ long uid; ++ char comm[64]; ++}; ++ + /** + * struct task_cputime - collected CPU time counts + * @utime: time spent in user mode, in &cputime_t units +diff -ru linux-3.5.4/kernel/sched/core.c linux-3.5.4-compile/kernel/sched/core.c +--- linux-3.5.4/kernel/sched/core.c 2012-09-15 00:28:08.000000000 +0200 ++++ linux-3.5.4-compile/kernel/sched/core.c 2012-09-30 11:01:31.395166872 +0200 +@@ -4453,6 +4453,80 @@ + return retval; + } + ++/* ++ * Does a depth first search through the entire process tree. ++ * nr is a pointer to the number of entries that should be put ++ * into the address specified by buf. nr is set to the number ++ * of entries actually put into that buffer. ++ */ ++SYSCALL_DEFINE2(ptree, struct prinfo *, buf, unsigned int *, nr) ++{ ++ unsigned int count, max; ++ int ret, moved_up; ++ struct prinfo info; ++ struct task_struct *task, *child, *sibling; ++ ++ ret = 0; ++ count = 0; ++ ++ if (!nr) ++ return -EINVAL; ++ if (get_user(max, nr)) { ++ ret = -EFAULT; ++ goto out; ++ } ++ if (!buf) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ memset(&info, 0, sizeof(info)); ++ task = &init_task; ++ moved_up = 0; ++ rcu_read_lock(); ++ while (task && count < max) { ++ child = list_empty(&task->children) ? NULL : list_first_entry(&task->children, struct task_struct, sibling); ++ sibling = list_empty(&task->sibling) ? NULL : list_first_entry(&task->sibling, struct task_struct, sibling); ++ ++ /* If we're not traversing back up the list, and we're not init_task, ++ * then this is a task we want. */ ++ if (!moved_up && task->pid) { ++ info.state = task->state; ++ info.pid = task->pid; ++ info.parent_pid = task->parent->pid; ++ info.first_child_pid = child ? child->pid : 0; ++ info.next_sibling_pid = sibling ? sibling->pid : 0; ++ info.uid = task->cred->uid; ++ get_task_comm(info.comm, task); ++ if (copy_to_user(&(buf[count++]), &info, sizeof(struct prinfo))) { ++ ret = -EFAULT; ++ break; ++ } ++ } ++ /* If we have just moved back up the list, or if we're at a leaf node... */ ++ if (moved_up || !child) { ++ if (!task->parent) ++ break; ++ ++ /* If there are no other siblings to hang out with, move up another ++ * level to the parent. */ ++ if (list_is_last(&task->sibling, &task->parent->children)) { ++ task = task->parent; ++ moved_up = 1; ++ } else { /* Otherwise, check out the sibling. */ ++ task = sibling; ++ moved_up = 0; ++ } ++ } else /* Otherwise, next time examine the next child. */ ++ task = child; ++ } ++ rcu_read_unlock(); ++out: ++ /* nr should be the number of entries copied */ ++ *nr = count; ++ return ret; ++} ++ + /** + * sys_sched_setscheduler - set/change the scheduler policy and RT priority + * @pid: the pid in question. diff --git a/ramfs/src/ptree.c b/ramfs/src/ptree.c new file mode 100644 index 0000000..2773140 --- /dev/null +++ b/ramfs/src/ptree.c @@ -0,0 +1,79 @@ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/syscall.h> + +#define __NR_ptree 313 + +struct prinfo { + long state; + pid_t pid; + pid_t parent_pid; + pid_t first_child_pid; + pid_t next_sibling_pid; + long uid; + char comm[64]; +}; + +static inline int ptree(struct prinfo *processes, unsigned int *count) +{ + return syscall(__NR_ptree, processes, count); +} + +static pid_t find_parent(struct prinfo *processes, unsigned int count, pid_t pid) +{ + int i; + for (i = 0; i < count; ++i) { + if (processes[i].pid == pid) + return processes[i].parent_pid; + } + return 0; +} + +static int hilariously_inefficient_method_of_finding_indentation_level(struct prinfo *processes, unsigned int count, pid_t pid) +{ + int indentation = 0; + while((pid = find_parent(processes, count, pid))) + ++indentation; + return indentation; + + /* Bonus points if you can find a reasonable _looking_ + * algorithm that is even more inefficient. */ +} + +int main(int argc, char *argv[]) +{ + struct prinfo *processes; + unsigned int count, indentation, i, j; + pid_t last_ppid; + + indentation = 0; + + count = 32768; + processes = malloc(sizeof(struct prinfo) * count); + if (!processes) { + perror("processes"); + return EXIT_FAILURE; + } + + memset(processes, 0, sizeof(struct prinfo) * count); + + if (ptree(processes, &count)) { + perror("ptree"); + return EXIT_FAILURE; + } + + for (i = 0; i < count; ++i) { + indentation = hilariously_inefficient_method_of_finding_indentation_level(processes, count, processes[i].pid); + for (j = 0; j < indentation; ++j) + putchar('\t'); + printf("%s,%d,%ld,%d,%d,%d,%ld\n", processes[i].comm, processes[i].pid, + processes[i].state, processes[i].parent_pid, processes[i].first_child_pid, + processes[i].next_sibling_pid, processes[i].uid); + } + + printf("%d processes\n", count); + + return EXIT_SUCCESS; +} |
