Ben*_*rns 12 security time posix exploit clock
我正在编写一些高度可移植的安全代码.我试图避免实用程序中的安全漏洞,例如在某些版本中找到的这个sudo:
...通过运行
sudo -k然后将系统时钟重置为01-01-1970,可以成为超级用户.
发生这种情况是因为sudo依赖于绝对(即日历)时间来确定访问是否已超时.
我的想法是使用CLOCK_MONOTONIC定义的time.h.
从POSIX标准来看,
[CLOCK_MONOTONIC]被定义为一个时钟,其值不能通过clock_settime()设置,并且不能有后向时钟跳转.最大可能的时钟跳转应由实现定义.
问题是,在许多(大多数?)系统上,CLOCK_MONOTONIC重启时会重置.是否有任何保证 POSIX兼容的方法来确定自程序上次运行以来系统是否已重新启动?
一种(坏)方法是检查存储的时钟值是否大于当前时钟值,但这只会改变问题.在CLOCK_MONOTONIC重新启动时重置的系统上,可能会有一个TIMEOUT允许访问的短窗口.
我错过了什么可以避免这个问题?
小智 10
在我看来,使用POSIX 共享内存对象很简单:
POSIX共享内存对象具有内核持久性:共享内存对象将一直存在,直到系统关闭,或者直到所有进程都取消映射该对象并且已使用shm_unlink删除它
每当程序启动时,它都可以使用shm_open一个具有一致名称的新对象并将所有者设置为root.该对象不需要包含任何特定值.POSIX要求所有共享内存对象一直持续到重新引导,除非手动销毁(只有它的所有者或创建者可以做...在这种情况下是root用户).
每当程序启动时,它首先检查是否已存在以root作为所有者的共享内存对象.由于只有root可以创建这样的对象,并且只有root或重启可能会破坏它,因此您可以确定自上次重启后您的程序是否已经启动,保存唯一可能的规避是root用户shm_unlink手动调用该对象.
我在下面写了一个测试和设置函数,它应该完全符合你的需要.它的工作原理除了所有权设置/检测之外:由于某些未知原因,两个调用shmctl都在我的系统上失败,并说"无效参数".表示错误的man页面表示无效的内存对象标识符或无效的命令.但是和命令肯定是有效的,您可以观察程序的输出以查看每次创建和/或打开的有效对象标识符.shmctlEINVALIPC_SETIPC_STAT
#include <sys/shm.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <stdlib.h>
int rebooted_test_and_set() {
int err;
int rebooted;
struct shmid_ds shmst;
// create object if nonexistent, returning failure if already exists
int shmid = shm_open("/bootcheck", O_CREAT | O_EXCL);
if (shmid != -1) {
fprintf(stderr, "bootcheck object did not exist, so created: %d\n", shmid);
// object did not exist, so system has been rebooted
rebooted = 1;
// set owner to root, and no permissions for anyone
shmst.shm_perm.uid = 0;
shmst.shm_perm.gid = 0;
shmst.shm_perm.mode = 0;
if ((err = shmctl(shmid, IPC_SET, &shmst)) == -1) {
perror("shmctl: shmctl failed to set owner and permissions for bootcheck object");
exit(1);
}
} else {
// object already exists, so reopen with read access and verify that the owner is root
shmid = shm_open("/bootcheck", O_RDONLY);
if (shmid == -1) {
perror("shm_open: failed, perhaps due to insufficient privileges");
exit(1);
}
fprintf(stderr, "bootcheck object (%d) exists, so checking ownership\n", shmid);
if ((err = shmctl(shmid, IPC_STAT, &shmst)) == -1) {
perror("shmctl: shmctl failed");
exit(1);
}
if (shmst.shm_perm.uid == 0) {
// yes, the bootcheck owner is root,
// so we are confident the system has NOT been rebooted since last launch
rebooted = 0;
} else {
// uh oh, looks like someone created the object illegitimately.
// since that is only possible if the root-owned object did not exist,
// therefore we know that it never did exist since the last reboot
rebooted = 1;
}
}
return rebooted;
}
// for debugging purposes ONLY, so I don't have to keep rebooting to clear the object:
void rebooted_clear() {
if (shm_unlink("/bootcheck") == -1) {
perror("shm_unlink: failed, probably due to insufficient privileges or object nonexistent");
exit(1);
}
}
int main() {
int rebooted = rebooted_test_and_set();
printf("rebooted since last launch: %d\n", rebooted);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
如果有人有任何线索,我很难过.一些信息和示例POSIX共享内存在这里.