- VisualStudio2022插件的安装及使用-编程手把手系列文章
- pprof-在现网场景怎么用
- C#实现的下拉多选框,下拉多选树,多级节点
- 【学习笔记】基础数据结构:猫树
printenv
或env
指令来打印环境变量: 如果只想打印特定的环境变量,如PWD变量,可以使用printenv PWD或者env | grep PWD 。
export
和unset
来设置或者取消环境变量export
设置环境变量: 比如现在我使用export设置一个环境变量MY_VAR的值为softwaresecurity 。
可以使用echo $MY_VAR打印出这个环境变量的值.
unset
取消环境变量: 取消变量MY_VAR.
myprintenv.c
并运行,将输出结果打印到文件output1.txt
中。printenv()
,并取消注释父进程的printenv()
,再次编译并打印输出到文件output2.txt
。diff
命令比较两个文件的差异。 结论:由于我在不同的窗口下运行的a.out和b.out,因此父子进程只有编译成的可执行文件名称和命令行窗口这两个环境变量不同,其余的环境变量都是相同的。结论是子进程在继承父进程的环境变量时,除了文件名和输出窗口存在差异以外,其他的环境变量都是相同的.
execve()
myenv.c
发现输出为空.
execve()
函数为execve("/usr/bin/env",argv,environ);
发现打印出了当前进程的环境变量.
结论:
execve()函数的原型是:
int execve(const char *pathname, char *const argv[], char *const envp[]);
pathname
: 要执行的程序的路径。argv
: 参数数组,以 NULL
结尾,包含传递给程序的命令行参数。envp
: 环境变量数组,也以 NULL
结尾。新程序通过execve()函数的第三个参数传递的environ变量来获取环境变量.
system()
编译并运行如下代码:
#include <stdio.h>
#include <stdlib.h>
int main()
{
system("/usr/bin/env");
return 0;
}
我们使用man system查看函数的手册:
可以看到system()函数是通过创建一个子进程,执行execl("/bin/sh", "sh", "-c", command, (char *) NULL);,调用进程的环境变量会传递给新程序/bin/sh.
Set-UID
程序#include <stdio.h>
#include <stdlib.h>
extern char **environ;
void main()
{
int i = 0;
while (environ[i] != NULL) {
printf("%s\n", environ[i]);
i++;
}
}
// Asssume the program’s name is foo
$ sudo chown root foo
$ sudo chmod 4755 foo
查看一下foo的权限,发现所有者更改为了root.
然后运行foo并查看这些环境变量的值 。
发现只有在父进程中设置的PATH和MY_NAME的环境变量进入子进程,而LD_LIBRARY_PATH这个环境变量没有进入子进程.
LD_LIBRARY_PATH这个环境变量设置的是动态链接器的地址,由于动态链接器的保护机制,虽然在一个root权限的程序下创建子进程并继承父进程的环境变量,但由于我们是在普通用户下修改的LD_LIBRARY_PATH这个环境变量,所以是无法在子进程中生效的,而PATH和MY_NAME则没有这种保护机制,因此可以被成功设置.
PATH
环境变量和Set-UID
程序先使用以下命令将bin/sh链接到bin/zsh,以规避bin/dash阻止Set-UID程序使用特权执行的策略.
sudo ln -sf /bin/zsh /bin/sh
然后编写LS.c文件,如下所示:
#include<stdio.h>
#include<stdlib.h>
int main(){
system("ls");
return 0;
}
然后编译,并设置为Set-UID程序:
可以看出,编译出来的LS文件确实执行了system("ls")的操作,更改后的文件所有者确实变成了root 。
现在我们在普通用户下设置PATH环境变量,使用export PATH=/home/seed:$PATH将/home/seed 添加到环境变量的开头:
然后我们在/home/seed下编写我们的恶意代码.
// hack.c
#include<stdio.h>
#include<stdlib.h>
#include <unistd.h>
extern char **environ;
int main(){
uid_t euid = geteuid(); //获取执行恶意代码的进程的euid
printf("euid=%d\n", euid);
printf("You have been hacked!!!!\n");
return 0;
}
然后编译并命名成ls:
gcc hack.c -o ls
然后再执行我们的LS文件:
发现可以使用Set-UID程序运行我们的恶意代码,并且根据system("id")的结果来看:euid=0表示当前进程具有root权限,表明恶意代码是以root权限运行的.
LD_PRELOAD
环境变量和Set-UID
程序mylib.c
,里面基本上覆盖了libc里的sleep()
函数:#include <stdio.h>
void sleep (int s)
{
/* If this is invoked by a privileged program ,
you can do damages here! */
printf("I am not sleeping!\n");
}
gcc -fPIC -g -c mylib.c
gcc -shared -o libmylib.so.1.0.1 mylib.o -lc
LD_PRELOAD
环境变量的值:export LD_PRELOAD=./libmylib.so.1.0.1
myprog.c
/* myprog.c */
#include <unistd.h>
int main()
{
sleep(1);
return 0;
}
发现执行的是我们编写的sleep函数.
发现等待了一秒后,没有输出,说明执行的是libc中的sleep()函数.
发现执行的是我们编写的sleep函数.
发现等待了一秒后,没有输出,说明执行的是libc中的sleep()函数.
修改一下myprog.c,打印这个程序运行时的进程的uid、euid以及LD_PRELOAD环境变量的值,如下所示:
/* myprog.c */
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
extern char **environ;
int main()
{
sleep(1);
uid_t uid = getuid();
printf("uid=%d(%s) ", uid, getenv("USER"));
uid_t euid = geteuid();
printf("euid=%d\n", euid);
char *preload = getenv("LD_PRELOAD");
printf("LD_PRELOAD: %s\n", preload);
return 0;
}
然后编写一个shell脚本,用于测试四种情况的输出以及当前进程的id,如下所示:
#test.sh
echo "seed,run in seed:"
sudo chown seed myprog
sudo chmod 4755 myprog
export LD_PRELOAD=./libmylib.so.1.0.1
./myprog
echo "root,run in seed:"
sudo chown root myprog
sudo chmod 4755 myprog
./myprog
echo "root,run in root:"
sudo su <<EOF
export LD_PRELOAD=./libmylib.so.1.0.1
./myprog
EOF
echo "user1,run in seed:"
sudo chown user1 myprog
sudo chmod 4755 myprog
export LD_PRELOAD=./libmylib.so.1.0.1
./myprog
这个脚本可以自动化测试四种情况下的sleep()函数的执行情况以及打印当前进程的id,运行结果如下:
我们发现:
当myprog为一个普通程序,以普通用户身份执行它时,其uid为seed,euid也为seed,LD_PRELOAD环境变量继承了父进程的,并且执行的是我们编写的sleep函数.
当myprog为一个Set-UID程序时,以普通用户身份执行它时,其uid为seed,euid为root,LD_PRELOAD环境变量没有继承父进程的,并且执行的是libc的sleep函数.
当myprog为一个Set-UID程序时,以root用户身份执行它时,其uid为root,euid为root,LD_PRELOAD环境变量继承了父进程的,并且执行的是我们编写的sleep函数.
当myprog为一个Set-UID user1程序时,以普通用户身份执行它时,其uid为seed,euid为user1,LD_PRELOAD环境变量没有继承父进程的,并且执行的是libc的sleep函数.
如下表所示:
程序类型 | 执行用户 | uid | euid | LD_PRELOAD环境变量 | 执行的sleep函数 |
---|---|---|---|---|---|
普通程序 | seed | seed | seed | 继承父进程 | 我们编写的 |
Set-UID程序 | seed | seed | root | 没有继承父进程 | libc的 |
Set-UID程序 | root | root | root | 继承父进程 | 我们编写的 |
Set-UID user1程序 | seed | seed | user1 | 没有继承父进程 | libc的 |
结论:
当一个进程的uid和euid一致时,子进程才会继承父进程的环境变量,才会执行我们编写的sleep()函数,第二步行为不同的原因是因为它们的uid和euid的一致/不一致会导致子进程继承/不继承LD_PRELOAD环境变量,从而导致了sleep()函数的不同.
编写并编译catcall.c,如下所示:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *v[3];
char *command;
if(argc < 2) {
printf("Please type a file name.\n");
return 1;
}
v[0] = "/bin/cat"; v[1] = argv[1]; v[2] = NULL;
command = malloc(strlen(v[0]) + strlen(v[1]) + 2);
sprintf(command , "%s %s", v[0], v[1]);
system(command);
// execve(v[0], v, NULL);
return 0 ;
}
这个程序调用了system()函数执行了/bin/cat [filename],可以打印指定文件的内容.
编译上述程序,使其成为 root 所有的 Set-UID 程序。该程序将使用 system() 来调用该命令。如果你是 Bob,你能损害系统的完整性吗?例如,你可以删除对你没有写权限的文件吗?
尝试删除没有写权限的文件:
catcall
有命令注入漏洞,可以调用system()
执行其他系统命令: 使用命令catcall "test.txt;rm test.txt"成功将没有写权限的test.txt删除.
注释掉 system(command) 语句,取消注释 execve() 语句;程序将使用 execve() 来调用命令。 编译程序,并使其成为 root 拥有的 Set-UID 程序。你在第一步中的攻击仍然有效吗?请描述并解释你的观察结果.
catcall "test.txt;rm test.txt"
发现无法删除test.txt,攻击失效.
原理:
使用system()函数能成功删除的原因是system()函数会创建一个子进程,并调用bin/bash来执行函数的参数,因此执行catcall "test.txt;rm test.txt"就相当于父进程创建了一个子进程,子进程使用bin/bash执行bin/cat test.txt;rm test.txt ,由于bash的特性,分号后面会作为下一个命令并执行,而且父进程是一个Set-UID程序,因此相当于在 root 下执行了rm test.txt,所以可以删除文件.
而使用execve()函数删除不了文件的原因是execve()函数并不是调用bin/bash来执行函数的参数的,而是通过系统调用的方式执行bin/cat test.txt;rm test.txt,它会把 test.txt;rm test.txt 当作一个文件名,而我们这个目录下并不存在这个文件,因此会报错/bin/cat: 'test.txt;rm test.txt': No such file or directory 。
编译以下程序,将其所有者更改为 root,并使其成为 Set-UID 程序.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
void main()
{
int fd;
char *v[2];
/* Assume that /etc/zzz is an important system file,
* and it is owned by root with permission 0644.
* Before running this program, you should create
* the file /etc/zzz first. */
fd = open("/etc/zzz", O_RDWR | O_APPEND);
if (fd == -1) {
printf("Cannot open /etc/zzz\n");
exit(0);
}
// Print out the file descriptor value
printf("fd is %d\n", fd);
// Permanently disable the privilege by making the
// effective uid the same as the real uid
setuid(getuid());
// Execute /bin/sh
v[0] = "/bin/sh"; v[1] = 0;
execve(v[0], v, 0);
}
我们在/etc下创建文件zzz,并运行cap_leak 。
文件描述符(File Descriptor,简称 fd)是操作系统中用于管理和操作文件或其他输入/输出资源(如网络连接、管道等)的一个重要概念。当打开一个文件时,操作系统会返回一个文件描述符,后续的读写操作都通过这个描述符进行.
此时输出了zzz文件的文件描述符fd(File Descriptor),并且执行了setuid(getuid())操作,将进程的uid改为了当前用户的,也就是将uid设为seed,然后调用execve()函数执行了bin/sh开启了一个shell.
我们使用whoami命令查看shell的拥有者:
发现拥有者确实是seed,但是虽然这个进程的有效用户ID是 seed ,但是该进程仍然拥有特权,我们可以以普通用户的身份将恶意代码写入/etc/zzz文件中,这个过程需要利用文件描述符fd.
我们可以使用echo "You have been hacked!!" >& 3,将这段话通过文件描述符写入/etc/zzz:
可以发现成功写入了文件.
原理:
虽然代码中执行了setuid(getuid())操作,将进程的uid改为了seed,但是在执行execve(v[0], v, 0) 打开一个shell时,由于在放弃特权时没有关闭/etc/zzz这个文件,创建的子进程会继承/etc/zzz这个文件的文件描述符,造成特权泄露,子进程可以利用这个文件描述符向文件中写入内容.
最后此篇关于SEEDLab——环境变量与Set-UID实验的文章就讲到这里了,如果你想了解更多关于SEEDLab——环境变量与Set-UID实验的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
这个问题在这里已经有了答案: 关闭 10 年前。 Possible Duplicate: How to nest OR statements in JavaScript? 有没有办法做到这一点:
在 JavaScript 中有没有办法让一个变量总是等于一个变量?喜欢var1 = var2但是当var2更新,也是var1 . 例子 var var1 = document.getElementBy
我正在努力理解这代表什么 var1 = var2 == var3 我的猜测是这等同于: if (var2 == var3): var1 = var2 最佳答案 赋值 var1 = var2
这个问题已经有答案了: What does the PHP error message "Notice: Use of undefined constant" mean? (2 个回答) 已关闭 8
我在临时表中有几条记录,我想从每条记录中获取一个值并将其添加到一个变量中,例如 color | caption -------------------------------- re
如何将字符串转为变量(字符串变量--> $variable)? 或者用逗号分隔的变量列表然后转换为实际变量。 我有 2 个文件: 列名文件 行文件 我需要根据字符串匹配行文件中的整行,并根据列名文件命
我有一个我无法解决的基本 php 问题,我也想了解为什么! $upperValueCB = 10; $passNodeMatrixSource = 'CB'; $topValue= '$uppe
这可能吗? php $variable = $variable1 || $variable2? 如果 $variable1 为空则使用 $variable2 是否存在类似的东西? 最佳答案 PHP 5
在 Perl 5.20 中,for 循环似乎能够修改模块作用域的变量,但不能修改父作用域中的词法变量。 #!/usr/bin/env perl use strict; use warnings; ou
为什么这不起作用: var variable; variable = variable.concat(variable2); $('#lunk').append(variable) 我无法弄清楚这一点
根据我的理解,在32位机器上,指针的sizeof是32位(4字节),而在64位机器上,它是8字节。无论它们指向什么数据类型,它们都有固定的大小。我的计算机在 64 位上运行,但是当我打印包含 * 的大
例如: int a = 10; a += 1.5; 这运行得很完美,但是 a = a+1.5; 此作业表示类型不匹配:无法从 double 转换为 int。所以我的问题是:+= 运算符 和= 运算符
您好,我写了这个 MySQL 存储过程,但我一直收到这个语法错误 #1064 - You have an error in your SQL syntax; check the manual that
我试图在我的场景中显示特定的奖牌,这取决于你的高分是基于关卡的目标。 // Get Medal Colour if levelHighscore goalScore { sc
我必须维护相当古老的 Visual C++ 源代码的大型代码库。我发现代码如下: bIsOk = !!m_ptr->isOpen(some Parameters) bIsOk的数据类型是bool,is
我有一个从 MySQL 数据库中提取的动态产品列表。在 list 上有一个立即联系 按钮,我正在使用一个 jquery Modal 脚本,它会弹出一个表单。 我的问题是尝试将产品信息变量传递给该弹出窗
这个问题在这里已经有了答案: 关闭 10 年前。 Possible Duplicate: What is the difference between (type)value and type(va
jQuery Core Style Guidelines建议两种不同的方法来检查变量是否已定义。 全局变量:typeof variable === "undefined" 局部变量:variable
这个问题已经有答案了: 已关闭11 年前。 Possible Duplicate: “Variable” Variables in Javascript? 我想肯定有一种方法可以在 JavaScrip
在语句中使用多重赋值有什么优点或缺点吗?在简单的例子中 var1 = var2 = true; 赋值是从右到左的(我相信 C# 中的所有赋值都是如此,而且可能是 Java,尽管我没有检查后者)。但是,
我是一名优秀的程序员,十分优秀!