DF-3017 / hunt2.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | #!/usr/bin/env python3 """Lean hunt: panicking cpu=1, one big stack read, targeted cdev chase.""" import socket, time, sys sys.path.insert(0, "/home/maxx/dfbsd/dfbsd/findings/poc/DF-3017") _sock = socket.create_connection(("127.0.0.1", 1235), timeout=10) _sock.settimeout(2.0) def rsp(cmd, timeout=2.0): pkt = b"$" + cmd + b"#%02x" % (sum(cmd) & 0xFF) _sock.sendall(pkt) buf = b"" t0 = time.time() while time.time() - t0 < timeout: try: d = _sock.recv(65536) except socket.timeout: break if not d: raise RuntimeError("eof") buf += d if buf.startswith(b"+$") or (buf.startswith(b"$") and b"#" in buf[1:]) \ or (b"$" in buf and b"#" in buf[buf.index(b"$")+1:]): try: _sock.sendall(b"+") except Exception: pass i = buf.index(b"$"); j = buf.index(b"#", i + 1) return buf[i+1:j] raise RuntimeError("timeout: %r" % buf[:64]) # handshake _sock.sendall(b"$qSupported#37") time.sleep(0.3) try: _sock.recv(65536) except Exception: pass _sock.sendall(b"+") def rd(addr, n): body = rsp(b"m%x,%x" % (addr, n)) if body.startswith(b"E"): raise RuntimeError("mem err " + body.decode()) return bytes.fromhex(body.decode()) def rq(a): return int.from_bytes(rd(a, 8), "little") rsp(b"Hg2") # cpu1 (1-based) g = rsp(b"g") regs = {} names = ["rax","rbx","rcx","rdx","rsi","rdi","rbp","rsp","r8","r9","r10", "r11","r12","r13","r14","r15","rip"] for i, n in enumerate(names): regs[n] = int.from_bytes(bytes.fromhex(g[i*16:(i+1)*16].decode()), "little") print("cpu1 rip=%x rbp=%x rsp=%x" % (regs["rip"], regs["rbp"], regs["rsp"])) fp = regs["rbp"] frames = [] for i in range(10): ret = rq(fp + 8) frames.append((fp, ret)) fp = rq(fp) if fp == 0: break for f, r in frames: print(" frame %x ret=%x" % (f, r)) # read the whole stack span once lo = min(f for f, _ in frames) - 0x600 span = rd(lo, 0x1800) DEAD = 0xffffffff81187fe0 def looks_cdev(q): try: ops = rq(q + 168) if ops in (DEAD, 0x40000000): return ops if 0xffffffff810e0000 <= ops < 0xffffffff810f0000: # pts98_ops area return ops except Exception: pass return None seen = set() for off in range(0, len(span) - 8, 8): v = int.from_bytes(span[off:off+8], "little") if 0xfffff80000000000 <= v < 0xfffff81000000000: for d in (0, -0x140, -0x100, -0xc0, -0x80, -0x40, 0x40, 0x80, 0xc0, 0x100, 0x140): q = (v & ~7) + d if q in seen: continue seen.add(q) ops = looks_cdev(q) if ops: try: nm = rd(q + 88, 12) refw = rq(q + 232) zbase = q & ~0x1ffff zcpu = None try: zcpu = int.from_bytes(rd(zbase + 4, 4), "little") except Exception: pass print("[CDEV] %x ops=%x name=%r refcnt=%x flags=%x zone=%x z_Cpu=%s" % (q, ops, nm, refw & 0xffffffff, refw >> 32, zbase, zcpu)) except Exception as e: print("[CDEV] %x ops=%x (detail err %s)" % (q, ops, e)) |