Commit e2272978 authored by jan.koester's avatar jan.koester
Browse files

test

parent d59e44f6
Loading
Loading
Loading
Loading
Loading
+29 −9
Changes for plugin/smb/smb.cpp: 29 added lines, 9 removed lines.
Original line number Diff line number Diff line
@@ -63,17 +63,26 @@ void RpcPipe::handlePdu(const std::vector<uint8_t> &pdu) {
    if (h.pduType == rpc::PDU_BIND || h.pduType == rpc::PDU_ALTER) {
        rpc::BindPdu b;
        if (rpc::parseBind(pdu.data(), pdu.size(), b) && !b.contexts.empty()) {
            std::cerr << "[RPC] BIND auth=" << b.hasAuth << " type=" << int(b.authType)
                      << " level=" << int(b.authLevel) << " token=" << b.authToken.size() << "B"
                      << " iface=" << _name << std::endl;
            _boundAbstract = b.contexts[0].abstractSyntax;
            _bound = true;
            resp = rpc::buildBindAck(b, "\\PIPE\\" + _name);
        } else {
            resp = rpc::buildBindNak(h.callId);
        }
    } else if (h.pduType == rpc::PDU_AUTH3) {
        std::cerr << "[RPC] AUTH3 received (authLen=" << h.authLength << ")" << std::endl;
    } else if (h.pduType == rpc::PDU_REQUEST) {
        rpc::RequestPdu req;
        if (rpc::parseRequest(pdu.data(), pdu.size(), req))
        if (rpc::parseRequest(pdu.data(), pdu.size(), req)) {
            if (req.hasAuth)
                std::cerr << "[RPC] REQUEST opnum=" << req.opnum << " auth type=" << int(req.authType)
                          << " level=" << int(req.authLevel) << " token=" << req.authToken.size() << "B" << std::endl;
            resp = dispatch(req);
        }
    }
    _out.insert(_out.end(), resp.begin(), resp.end());
}

@@ -85,7 +94,7 @@ std::vector<uint8_t> RpcPipe::dispatch(const rpc::RequestPdu &req) {
        res = rpc::lsarpcDispatch(req.opnum, req.stub, _sessionKey);
    } else if (rpc::isSamr(_boundAbstract.uuid)) {
        iface = "samr";
        res = rpc::samrDispatch(req.opnum, req.stub, _sessionKey);
        res = rpc::samrDispatch(req.opnum, req.stub, _sessionKey, _appKey);
    } else if (rpc::isNetlogon(_boundAbstract.uuid)) {
        iface = "netlogon";
        res = rpc::netlogonDispatch(req.opnum, req.stub, _sessionKey);
@@ -383,8 +392,8 @@ std::vector<uint8_t> SmbServer::handleMessage(Conn &c, const std::vector<uint8_t
                              << "' -> UNKNOWN USER" << std::endl;
                    return errorResponse(req, smb::STATUS_LOGON_FAILURE);
                }
                std::vector<uint8_t> exported;
                if (!ntlm::verifyV2(ntHash, a, c.ntlmChallenge, exported)) {
                std::vector<uint8_t> exported, baseKey;
                if (!ntlm::verifyV2(ntHash, a, c.ntlmChallenge, exported, &baseKey)) {
                    std::cerr << "[SMB] NTLMSSP AUTH '" << a.domain << "\\" << a.user
                              << "' -> BAD PASSWORD" << std::endl;
                    return errorResponse(req, smb::STATUS_LOGON_FAILURE);
@@ -394,6 +403,7 @@ std::vector<uint8_t> SmbServer::handleMessage(Conn &c, const std::vector<uint8_t
                c.authenticated = true;
                c.clientPrincipal = princ;
                c.sessionKey = exported;
                c.ntlmBaseKey = baseKey;
                std::vector<uint8_t> outTok = spnego
                    ? gss::buildSpnegoResp(gss::ACCEPT_COMPLETED, nullptr, {}, {})
                    : std::vector<uint8_t>();
@@ -492,11 +502,21 @@ std::vector<uint8_t> SmbServer::handleMessage(Conn &c, const std::vector<uint8_t
            return errorResponse(req, smb::STATUS_OBJECT_NAME_NOT_FOUND);
        uint64_t fid = c.nextFid++;
        c.pipes[fid] = new RpcPipe(pname);
        /* RPC-over-SMB session key (used e.g. to decrypt SAMR passwords): for
         * SMB 3.x it is the derived application key (SMBAppKey), not the raw
         * authentication session key. */
        c.pipes[fid]->setSessionKey((c.dialect >= smb::SMB_3_0 && c.haveKeys)
                                        ? c.keys.application : c.sessionKey);
        std::cerr << "[SMB] CREATE pipe='" << pname << "' haveKeys=" << c.haveKeys
                  << " principal='" << c.clientPrincipal << "' sessKey=" << c.sessionKey.size()
                  << "B app=" << c.keys.application.size() << "B kek=" << c.ntlmBaseKey.size()
                  << "B dialect=0x" << std::hex << c.dialect << std::dec << std::endl;
        /* RPC-over-SMB session key (used e.g. to decrypt SAMR passwords):
         * provide both the raw auth session key and the derived SMBAppKey so
         * the SAMR layer can pick whichever the client used. */
        c.pipes[fid]->setSessionKey(c.sessionKey);
        {
            std::vector<uint8_t> blob;
            for (auto *k : {&c.keys.application, &c.keys.signing, &c.keys.encryption,
                            &c.keys.decryption, &c.ntlmBaseKey})
                if (k->size() >= 16) blob.insert(blob.end(), k->begin(), k->begin()+16);
            c.pipes[fid]->setAppKey(blob);
        }
        smb::Smb2Header h; h.command=req.command; h.messageId=req.messageId; h.sessionId=c.sessionId;
        h.treeId=req.treeId; h.status=smb::STATUS_SUCCESS; h.creditReqResp=1;
        auto resp = smb::buildHeader(h);
+3 −0
Changes for plugin/smb/smb.h: 3 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ namespace fenrirds {
        explicit RpcPipe(const std::string &name) : _name(name) {}
        const std::string &name() const { return _name; }
        void setSessionKey(const std::vector<uint8_t> &k) { _sessionKey = k; }
        void setAppKey(const std::vector<uint8_t> &k) { _appKey = k; }

        /* feed bytes written to the pipe; produce any complete PDU responses */
        void write(const uint8_t *data, size_t len);
@@ -52,6 +53,7 @@ namespace fenrirds {
        rpc::SyntaxId        _boundAbstract;  /* the interface bound on this pipe */
        bool                 _bound = false;
        std::vector<uint8_t> _sessionKey;
        std::vector<uint8_t> _appKey;
    };

    /***************************************************************************
@@ -92,6 +94,7 @@ namespace fenrirds {
            bool     haveKeys = false;
            std::vector<uint8_t> sessionKey;     /* GSS session key for SAMR */
            std::vector<uint8_t> ntlmChallenge;  /* 8-byte server challenge (NTLMSSP) */
            std::vector<uint8_t> ntlmBaseKey;    /* NTLM SessionBaseKey (pre key-exch) */
            uint32_t treeIpc = 0;                /* IPC$ tree id (0 = none) */
            std::map<uint64_t, RpcPipe*> pipes;  /* volatile file id -> pipe */
            uint64_t nextFid = 1;
+3 −1
Changes for src/crypto/ntlm.cpp: 3 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -85,7 +85,8 @@ bool parseAuth3(const std::vector<uint8_t> &t, Auth3 &o) {
}

bool verifyV2(const std::vector<uint8_t> &ntHash, const Auth3 &a,
              const std::vector<uint8_t> &serverChal, std::vector<uint8_t> &exportedKey) {
              const std::vector<uint8_t> &serverChal, std::vector<uint8_t> &exportedKey,
              std::vector<uint8_t> *sessionBaseKey) {
    if (a.ntResponse.size() < 16) return false;
    std::vector<uint8_t> ntProof(a.ntResponse.begin(), a.ntResponse.begin() + 16);
    std::vector<uint8_t> temp(a.ntResponse.begin() + 16, a.ntResponse.end());
@@ -98,6 +99,7 @@ bool verifyV2(const std::vector<uint8_t> &ntHash, const Auth3 &a,
    if (!crypto::equal(expect, ntProof)) return false;
    /* SessionBaseKey = KeyExchangeKey = HMAC-MD5(v2, NTProofStr) */
    auto kek = crypto::hmacMd5(v2, ntProof);
    if (sessionBaseKey) *sessionBaseKey = kek;
    if ((a.flags & 0x40000000u) && a.encSessionKey.size() == 16)   /* KEY_EXCH */
        exportedKey = crypto::rc4(kek, a.encSessionKey);
    else
+2 −1
Changes for src/crypto/ntlm.h: 2 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -38,7 +38,8 @@ namespace fenrirds {
            /* Verify NTLMv2; on success returns the exported (SMB/signing) key. */
            bool verifyV2(const std::vector<uint8_t> &ntHash, const Auth3 &a,
                          const std::vector<uint8_t> &serverChallenge,
                          std::vector<uint8_t> &exportedKey);
                          std::vector<uint8_t> &exportedKey,
                          std::vector<uint8_t> *sessionBaseKey = nullptr);

            /*****************************************************************
             * NTLM2 (extended session security) sign+seal message layer,
+64 −10
Changes for src/rpc/samr.cpp: 64 added lines, 10 removed lines.
Original line number Diff line number Diff line
@@ -124,7 +124,8 @@ namespace {
}

DispatchResult samrDispatch(uint16_t opnum, const std::vector<uint8_t> &stub,
                            const std::vector<uint8_t> &sessionKey) {
                            const std::vector<uint8_t> &sessionKey,
                            const std::vector<uint8_t> &appKey) {
    DispatchResult r;
    try {
        switch (opnum) {
@@ -304,18 +305,71 @@ DispatchResult samrDispatch(uint16_t opnum, const std::vector<uint8_t> &stub,
                /* SAMR_USER_CONTROL_INFORMATION: u32 acct_flags (ACB_*) */
                uint32_t acb = in.u32();
                dsdb::setInt(attrs, "userAccountControl", int32_t(acbToUac(acb)));
            } else if ((level == 25 || level == 26 || level == 24 || level == 23) && sessionKey.size() >= 16) {
            } else if ((level == 25 || level == 26 || level == 24 || level == 23)
                       && (sessionKey.size() >= 16 || appKey.size() >= 16)) {
                /* the buffer is a 516-byte SAMPR_USER_PASSWORD encrypted with
                 * RC4(session key) (levels 24/25) ... level 25/26 include extra
                 * fields; we take the last 516 bytes of the remaining stub. */
                 * RC4(session key); the exact "session key" over SMB3 is
                 * transport-specific, so try both the raw session key and the
                 * SMBAppKey and take whichever yields a valid password length. */
                std::vector<uint8_t> rest = in.takeVec(in.remaining());
                if (rest.size() >= 516) {
                    std::vector<uint8_t> enc(rest.end() - 516, rest.end());
                    auto key16 = std::vector<uint8_t>(sessionKey.begin(), sessionKey.begin() + 16);
                    auto dec = crypto::rc4(key16, enc);
                    uint32_t pwLen = dec[512] | dec[513] << 8 | dec[514] << 16 | uint32_t(dec[515]) << 24;
                    std::cerr << "[SAMR] SetUserInfo2 level=" << level << " sessKey=" << sessionKey.size()
                              << "B rest=" << rest.size() << "B pwLen=" << pwLen << std::endl;
                    /* The 516-byte SAMPR_ENCRYPTED_USER_PASSWORD sits between the
                     * SAMPR_USER_ALL_INFORMATION fixed part and its deferred
                     * buffers, so it is NOT simply the last 516 bytes. Scan all
                     * window offsets x both candidate keys; the correct one has
                     * an even password length in (0,512] at offset 512. */
                    std::vector<uint8_t> dec;
                    uint32_t pwLen = 0xffffffff;
                    /* candidate base keys: the raw SMB session key plus every
                     * 16-byte key in the appKey blob (SMBAppKey/signing/...). */
                    std::vector<std::vector<uint8_t>> cands;
                    if (sessionKey.size() >= 16) cands.emplace_back(sessionKey.begin(), sessionKey.begin()+16);
                    for (size_t o = 0; o + 16 <= appKey.size(); o += 16)
                        cands.emplace_back(appKey.begin()+o, appKey.begin()+o+16);
                    /* The 516-byte SAMPR_USER_PASSWORD sits between I1's fixed part
                     * and its deferred buffers; scan all offsets. Two encodings:
                     *  - level 25 (SAMPR_ENCRYPTED_USER_PASSWORD): RC4(key).
                     *  - level 26 (..._NEW): a 16-byte ClearSalt follows the 516
                     *    bytes and the RC4 key is MD5(ClearSalt || baseKey). */
                    for (size_t ci = 0; ci < cands.size() && pwLen > 512; ++ci) {
                        const auto &base = cands[ci];
                        for (size_t off = 0; off + 516 <= rest.size() && pwLen > 512; ++off) {
                            std::vector<uint8_t> blk(rest.begin()+off, rest.begin()+off+516);
                            /* level 26: ClearSalt right after the 516-byte block */
                            if (off + 532 <= rest.size()) {
                                std::vector<uint8_t> salted(rest.begin()+off+516, rest.begin()+off+532);
                                salted.insert(salted.end(), base.begin(), base.end());
                                auto d = crypto::rc4(crypto::md5(salted), blk);
                                uint32_t l = d[512] | d[513]<<8 | d[514]<<16 | uint32_t(d[515])<<24;
                                if (l > 0 && l <= 512 && (l % 2) == 0) {
                                    dec = std::move(d); pwLen = l;
                                    std::cerr << "[SAMR] SetUserInfo2 password(NEW) at off=" << off
                                              << " cand=" << ci << " pwLen=" << l << std::endl;
                                    break;
                                }
                            }
                            /* level 25: plain RC4(baseKey) */
                            auto d2 = crypto::rc4(base, blk);
                            uint32_t l2 = d2[512] | d2[513]<<8 | d2[514]<<16 | uint32_t(d2[515])<<24;
                            if (l2 > 0 && l2 <= 512 && (l2 % 2) == 0) {
                                dec = std::move(d2); pwLen = l2;
                                std::cerr << "[SAMR] SetUserInfo2 password at off=" << off
                                          << " cand=" << ci << " pwLen=" << l2 << std::endl;
                                break;
                            }
                        }
                    }
                    if (pwLen > 512) {
                        std::cerr << "[SAMR] SetUserInfo2 password NOT located (rest="
                                  << rest.size() << "B)" << std::endl;
                        static const char *hc="0123456789abcdef";
                        std::string hx; for (uint8_t b : rest){ hx+=hc[b>>4]; hx+=hc[b&0xf]; }
                        std::cerr << "[SAMR] rest=" << hx << std::endl;
                        std::string ks; for (size_t i=0;i<sessionKey.size()&&i<16;i++){ ks+=hc[sessionKey[i]>>4]; ks+=hc[sessionKey[i]&0xf]; }
                        std::cerr << "[SAMR] sessKey=" << ks << std::endl;
                        std::string ak; for (uint8_t b : appKey){ ak+=hc[b>>4]; ak+=hc[b&0xf]; }
                        std::cerr << "[SAMR] appKeyBlob=" << ak << std::endl;
                    }
                    if (pwLen <= 512) {
                        std::string pw = crypto::fromUtf16le(dec.data() + (512 - pwLen), pwLen);
                        std::string sam = dsdb::value(attrs, "sAMAccountName");
Loading