shadow1ng/fscan · error

failed to get OS name: %s

Error message

failed to get OS name: %s

What it means

This error wraps a failure of getOSName after the anonymous login reply is received (plugins/services/ms17010_exp.go:155). getOSName parses the UTF-16 native-OS string that starts 10 bytes after the SMB header (raw[smbHeaderSize+10:]) and scans until a 0x0000 terminator; it returns an error when the buffer runs out before finding one (io.ErrUnexpectedEOF / io.EOF from io.ReadFull). The library throws it because the login response did not carry a well-formed native OS string.

Source

Thrown at plugins/services/ms17010_exp.go:155

		return nil, nil, fmt.Errorf("failed to connect host: %s", err)
	}
	var ok bool
	defer func() {
		if !ok {
			_ = conn.Close()
		}
	}()
	err = smbClientNegotiate(conn)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to negotiate: %s", err)
	}
	raw, header, err := smb1AnonymousLogin(conn)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to login with anonymous: %s", err)
	}
	_, err = getOSName(raw)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to get OS name: %s", err)
	}
	//fmt.Println("OS:", osName)
	header, err = treeConnectAndX(conn, address, header.UserID)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to tree connect AndX: %s", err)
	}
	ok = true
	return header, conn, nil
}

const smbHeaderSize = 32

type smbHeader struct {
	ServerComponent [4]byte
	SMBCommand      uint8
	ErrorClass      uint8
	Reserved        byte
	ErrorCode       uint16

View on GitHub (pinned to 95cc12e753)

Solutions

  1. Dump the raw login response (hexdump) to confirm whether a Native OS string is present at the expected offset
  2. Treat hosts failing this parse as non-Windows/odd SMB stacks and skip them for EternalBlue
  3. Make the parser tolerant: check len(raw) >= smbHeaderSize+10 before scanning and return a descriptive error if too short
  4. Verify the login actually succeeded (NT status in the header) before attempting the OS-string parse

Example fix

// before
_, err = getOSName(raw)
if err != nil {
    return nil, nil, fmt.Errorf("failed to get OS name: %s", err)
}
// after
if len(raw) < smbHeaderSize+10 {
    return nil, nil, fmt.Errorf("login response too short for OS name: %d bytes", len(raw))
}
if _, err = getOSName(raw); err != nil {
    return nil, nil, fmt.Errorf("failed to get OS name: %w", err)
}
Defensive patterns

Strategy: type-guard

Validate before calling

func hasOSNameField(raw []byte) bool {
    return len(raw) >= smbHeaderSize+10 // parser starts at raw[smbHeaderSize+10:]
}

Type guard

func parseableLoginResponse(raw []byte) bool {
    if len(raw) < smbHeaderSize+10 { return false }
    return bytes.Equal(raw[0:4], []byte{0xFF, 0x53, 0x4D, 0x42}) // \xffSMB
}

Try / catch

raw, header, err := smb1AnonymousLogin(conn)
if err == nil && !parseableLoginResponse(raw) {
    // skip OS-name parsing; header may still be usable
}
_, err = getOSName(raw)
if err != nil {
    return fmt.Errorf("non-standard SMB stack (no Native OS string): %w", err)
}

Prevention

When it happens

Trigger: smb1AnonymousConnectIPC receives a login response whose bytes after offset smbHeaderSize+10 do not contain the expected UTF-16 string terminated by 0x0000 — e.g. the response is too short (near or below 42 bytes) or the layout deviates (server sent an error response instead of a session-setup reply).

Common situations: Non-Windows or embedded SMB implementations (Samba variants, NAS, printers) that omit or relocate the Native OS field; servers replying with a DOS error-format response rather than the expected AndX reply; truncated responses from flaky networks.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


AI-assisted analysis of shadow1ng/fscan@95cc12e753 (2026-09-06). Data as JSON: /api/errors/f35273cc5f31c952. Report an issue: GitHub.