Loading plugin/smb/smb.cpp +29 −9 Changes for plugin/smb/smb.cpp: 29 added lines, 9 removed lines. Original line number Diff line number Diff line Loading @@ -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()); } Loading @@ -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); Loading Loading @@ -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); Loading @@ -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>(); Loading Loading @@ -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); Loading plugin/smb/smb.h +3 −0 Changes for plugin/smb/smb.h: 3 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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; }; /*************************************************************************** Loading Loading @@ -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; Loading src/crypto/ntlm.cpp +3 −1 Changes for src/crypto/ntlm.cpp: 3 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -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()); Loading @@ -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 Loading src/crypto/ntlm.h +2 −1 Changes for src/crypto/ntlm.h: 2 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -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, Loading src/rpc/samr.cpp +64 −10 Changes for src/rpc/samr.cpp: 64 added lines, 10 removed lines. Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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 Loading
plugin/smb/smb.cpp +29 −9 Changes for plugin/smb/smb.cpp: 29 added lines, 9 removed lines. Original line number Diff line number Diff line Loading @@ -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()); } Loading @@ -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); Loading Loading @@ -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); Loading @@ -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>(); Loading Loading @@ -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); Loading
plugin/smb/smb.h +3 −0 Changes for plugin/smb/smb.h: 3 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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; }; /*************************************************************************** Loading Loading @@ -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; Loading
src/crypto/ntlm.cpp +3 −1 Changes for src/crypto/ntlm.cpp: 3 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -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()); Loading @@ -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 Loading
src/crypto/ntlm.h +2 −1 Changes for src/crypto/ntlm.h: 2 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -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, Loading
src/rpc/samr.cpp +64 −10 Changes for src/rpc/samr.cpp: 64 added lines, 10 removed lines. Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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