mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2026-11-13 05:41:25 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8903942700 | ||
|
|
aec1420bdb |
No files matched your search
@@ -1,6 +1,6 @@
|
||||
module github.com/projectdiscovery/nuclei/v3
|
||||
|
||||
go 1.27.3
|
||||
go 1.27.4
|
||||
|
||||
require (
|
||||
carvel.dev/ytt v0.52.0
|
||||
@@ -46,7 +46,6 @@ require (
|
||||
github.com/go-pdf/fpdf v0.9.0
|
||||
github.com/go-pg/pg/v10 v10.15.0
|
||||
github.com/go-playground/validator/v10 v10.26.0
|
||||
github.com/go-rod/rod v0.116.2
|
||||
github.com/go-sql-driver/mysql v1.10.0
|
||||
github.com/gobwas/ws v1.4.0
|
||||
github.com/goccy/go-json v0.10.5
|
||||
@@ -102,6 +101,7 @@ require (
|
||||
github.com/projectdiscovery/rdap v0.9.0
|
||||
github.com/projectdiscovery/retryabledns v1.0.116
|
||||
github.com/projectdiscovery/retryablehttp-go v1.3.31
|
||||
github.com/projectdiscovery/rod v0.116.3-0.20261113150229-d775599ff53d
|
||||
github.com/projectdiscovery/sarif v0.1.0
|
||||
github.com/projectdiscovery/tlsx v1.4.3-0.20261020052910-acb80c2c7c75
|
||||
github.com/projectdiscovery/uncover v1.2.1
|
||||
@@ -427,11 +427,7 @@ require (
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||
github.com/ysmood/fetchup v0.2.3 // indirect
|
||||
github.com/ysmood/goob v0.4.0 // indirect
|
||||
github.com/ysmood/got v0.40.0 // indirect
|
||||
github.com/ysmood/gson v0.7.3 // indirect
|
||||
github.com/ysmood/leakless v0.9.0 // indirect
|
||||
github.com/yuin/goldmark v1.7.17 // indirect
|
||||
github.com/yuin/goldmark-emoji v1.0.6 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
|
||||
@@ -493,8 +493,6 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
|
||||
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
||||
github.com/go-rod/rod v0.116.2 h1:A5t2Ky2A+5eD/ZJQr1EfsQSe5rms5Xof/qj296e+ZqA=
|
||||
github.com/go-rod/rod v0.116.2/go.mod h1:H+CMO9SCNc2TJ2WfrG+pKhITz57uGNYU43qYHh438Mg=
|
||||
github.com/go-sourcemap/sourcemap v2.1.4+incompatible h1:a+iTbH5auLKxaNwQFg0B+TCYl6lbukKPc7b5x0n1s6Q=
|
||||
github.com/go-sourcemap/sourcemap v2.1.4+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
|
||||
github.com/go-sql-driver/mysql v1.10.0 h1:Q+1LV8DkHJvSYAdR83XzuhDaTykuDx0l6fkXxoWCWfw=
|
||||
@@ -985,6 +983,8 @@ github.com/projectdiscovery/retryabledns v1.0.116 h1:tK28PJ4tLO7bgM89RpIFjkfTPMS
|
||||
github.com/projectdiscovery/retryabledns v1.0.116/go.mod h1:r3vH0AKYtJ9fculcqRFrdVYe2ABcHwZeR4C6j3rqCbY=
|
||||
github.com/projectdiscovery/retryablehttp-go v1.3.31 h1:0hSEZ5+jj6Oo1IL8qrytD36TK/qCxfe543jEaRqSTjs=
|
||||
github.com/projectdiscovery/retryablehttp-go v1.3.31/go.mod h1:/WEa7YejxTCXEMLCGfGYaC3JsgXgzuqw8rv8cb4buEc=
|
||||
github.com/projectdiscovery/rod v0.116.3-0.20261113150229-d775599ff53d h1:6mwNBdlYWe0hu1/QLLhIrZiz+G1JYsU7OIVmCOaF+q0=
|
||||
github.com/projectdiscovery/rod v0.116.3-0.20261113150229-d775599ff53d/go.mod h1:FLVLMgXw6OFcOU5nd4Il8cdSWgDfAlDeRZMol3drmGs=
|
||||
github.com/projectdiscovery/sarif v0.1.0 h1:O541T+a448nSJsmIMnXXSOeDQEzpnCAYvRfe0eG5h74=
|
||||
github.com/projectdiscovery/sarif v0.1.0/go.mod h1:LBC+reM3bkI3qIIhE0rZaINaYX6VG+En6u2hHa5mA7E=
|
||||
github.com/projectdiscovery/stringsutil v0.0.2 h1:uzmw3IVLJSMW1kEg8eCStG/cGbYYZAja8BH3LqqJXMA=
|
||||
@@ -1212,20 +1212,14 @@ github.com/yl2chen/cidranger v1.0.2 h1:lbOWZVCG1tCRX4u24kuM1Tb4nHqWkDxwLdoS+Seva
|
||||
github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g=
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
|
||||
github.com/ysmood/fetchup v0.2.3 h1:ulX+SonA0Vma5zUFXtv52Kzip/xe7aj4vqT5AJwQ+ZQ=
|
||||
github.com/ysmood/fetchup v0.2.3/go.mod h1:xhibcRKziSvol0H1/pj33dnKrYyI2ebIvz5cOOkYGns=
|
||||
github.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ=
|
||||
github.com/ysmood/goob v0.4.0/go.mod h1:u6yx7ZhS4Exf2MwciFr6nIM8knHQIE22lFpWHnfql18=
|
||||
github.com/ysmood/gop v0.2.0 h1:+tFrG0TWPxT6p9ZaZs+VY+opCvHU8/3Fk6BaNv6kqKg=
|
||||
github.com/ysmood/gop v0.2.0/go.mod h1:rr5z2z27oGEbyB787hpEcx4ab8cCiPnKxn0SUHt6xzk=
|
||||
github.com/ysmood/got v0.40.0 h1:ZQk1B55zIvS7zflRrkGfPDrPG3d7+JOza1ZkNxcc74Q=
|
||||
github.com/ysmood/got v0.40.0/go.mod h1:W7DdpuX6skL3NszLmAsC5hT7JAhuLZhByVzHTq874Qg=
|
||||
github.com/ysmood/gop v0.4.2 h1:KtW1/RugapWs1iW9ynQdk52BK6TWdP9zC0qDOhW8EDE=
|
||||
github.com/ysmood/gop v0.4.2/go.mod h1:K4LAB/BdLArknXrVSy+DyXcfb8P85JwAtwYM8zlb0mU=
|
||||
github.com/ysmood/got v0.43.0 h1:bcHlKg+qnhFz5JxNZ+j4fIfxxB8WWjTh6qdqeS8FfZw=
|
||||
github.com/ysmood/got v0.43.0/go.mod h1:rsfH2KJ5rcS/5n9pmybIGodoHlT/NTYxjgh3ob7UdSU=
|
||||
github.com/ysmood/gotrace v0.6.0 h1:SyI1d4jclswLhg7SWTL6os3L1WOKeNn/ZtzVQF8QmdY=
|
||||
github.com/ysmood/gotrace v0.6.0/go.mod h1:TzhIG7nHDry5//eYZDYcTzuJLYQIkykJzCRIo4/dzQM=
|
||||
github.com/ysmood/gson v0.7.3 h1:QFkWbTH8MxyUTKPkVWAENJhxqdBa4lYTQWqZCiLG6kE=
|
||||
github.com/ysmood/gson v0.7.3/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg=
|
||||
github.com/ysmood/leakless v0.9.0 h1:qxCG5VirSBvmi3uynXFkcnLMzkphdh3xx5FtrORwDCU=
|
||||
github.com/ysmood/leakless v0.9.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
package kerberos
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/jcmturner/gokrb5/v8/messages"
|
||||
"github.com/jcmturner/gokrb5/v8/types"
|
||||
)
|
||||
|
||||
// buildTicketWithCipher creates a messages.Ticket whose EncPart.Cipher has the
|
||||
// given length. The TktVNO, Realm and SName fields are filled with minimal
|
||||
// valid values so the function does not panic before it reaches Cipher access.
|
||||
func buildTicketWithCipher(cipherLen int) messages.Ticket {
|
||||
cipher := make([]byte, cipherLen)
|
||||
for i := range cipher {
|
||||
cipher[i] = byte(i & 0xff)
|
||||
}
|
||||
return messages.Ticket{
|
||||
TktVNO: 5,
|
||||
Realm: "TEST.REALM",
|
||||
SName: types.PrincipalName{
|
||||
NameType: 1,
|
||||
NameString: []string{"host", "server.test.realm"},
|
||||
},
|
||||
EncPart: types.EncryptedData{
|
||||
EType: 17,
|
||||
Cipher: cipher,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// buildASRepWithCipher creates a messages.ASRep whose EncPart.Cipher has the
|
||||
// given length.
|
||||
func buildASRepWithCipher(cipherLen int) messages.ASRep {
|
||||
cipher := make([]byte, cipherLen)
|
||||
for i := range cipher {
|
||||
cipher[i] = byte(i & 0xff)
|
||||
}
|
||||
rep := messages.ASRep{}
|
||||
rep.EncPart = types.EncryptedData{
|
||||
EType: 17,
|
||||
Cipher: cipher,
|
||||
}
|
||||
rep.CName = types.PrincipalName{
|
||||
NameType: 1,
|
||||
NameString: []string{"testuser"},
|
||||
}
|
||||
rep.CRealm = "TEST.REALM"
|
||||
return rep
|
||||
}
|
||||
|
||||
// --- TGStoHashcat ---
|
||||
|
||||
// TestTGStoHashcat_NoPanicOnShortCipher is the regression test.
|
||||
// Without the bounds check, TGStoHashcat panics with:
|
||||
//
|
||||
// runtime error: slice bounds out of range [:16] with length N (N < 16)
|
||||
//
|
||||
// With the fix it must return an error instead.
|
||||
func TestTGStoHashcat_NoPanicOnShortCipher(t *testing.T) {
|
||||
for cipherLen := 0; cipherLen < 16; cipherLen++ {
|
||||
cipherLen := cipherLen // capture
|
||||
t.Run("", func(t *testing.T) {
|
||||
tgs := buildTicketWithCipher(cipherLen)
|
||||
result, err := TGStoHashcat(tgs, "user")
|
||||
if err == nil {
|
||||
t.Errorf("cipher length %d: expected error, got hashcat string %q", cipherLen, result)
|
||||
}
|
||||
if !strings.Contains(err.Error(), "too short") {
|
||||
t.Errorf("cipher length %d: unexpected error message %q", cipherLen, err.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestTGStoHashcat_Valid_16Bytes confirms that a cipher of exactly 16 bytes succeeds.
|
||||
func TestTGStoHashcat_Valid_16Bytes(t *testing.T) {
|
||||
tgs := buildTicketWithCipher(16)
|
||||
result, err := TGStoHashcat(tgs, "user")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error for 16-byte cipher: %v", err)
|
||||
}
|
||||
if !strings.HasPrefix(result, "$krb5tgs$") {
|
||||
t.Errorf("unexpected hash format: %q", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestTGStoHashcat_Valid_Large confirms that a larger cipher also succeeds.
|
||||
func TestTGStoHashcat_Valid_Large(t *testing.T) {
|
||||
tgs := buildTicketWithCipher(256)
|
||||
result, err := TGStoHashcat(tgs, "user")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error for 256-byte cipher: %v", err)
|
||||
}
|
||||
if !strings.HasPrefix(result, "$krb5tgs$") {
|
||||
t.Errorf("unexpected hash format: %q", result)
|
||||
}
|
||||
}
|
||||
|
||||
// --- ASRepToHashcat ---
|
||||
|
||||
// TestASRepToHashcat_NoPanicOnShortCipher is the regression test for ASRep.
|
||||
func TestASRepToHashcat_NoPanicOnShortCipher(t *testing.T) {
|
||||
for cipherLen := 0; cipherLen < 16; cipherLen++ {
|
||||
cipherLen := cipherLen
|
||||
t.Run("", func(t *testing.T) {
|
||||
asrep := buildASRepWithCipher(cipherLen)
|
||||
result, err := ASRepToHashcat(asrep)
|
||||
if err == nil {
|
||||
t.Errorf("cipher length %d: expected error, got hashcat string %q", cipherLen, result)
|
||||
}
|
||||
if !strings.Contains(err.Error(), "too short") {
|
||||
t.Errorf("cipher length %d: unexpected error message %q", cipherLen, err.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestASRepToHashcat_Valid_16Bytes confirms that a cipher of exactly 16 bytes succeeds.
|
||||
func TestASRepToHashcat_Valid_16Bytes(t *testing.T) {
|
||||
asrep := buildASRepWithCipher(16)
|
||||
result, err := ASRepToHashcat(asrep)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error for 16-byte cipher: %v", err)
|
||||
}
|
||||
if !strings.HasPrefix(result, "$krb5asrep$") {
|
||||
t.Errorf("unexpected hash format: %q", result)
|
||||
}
|
||||
}
|
||||
@@ -167,11 +167,6 @@ func sendUDP(conn *net.UDPConn, b []byte) ([]byte, error) {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// maxKDCResponseSize caps the per-response allocation for a KDC TCP reply.
|
||||
// RFC 4120 §7.2.2 uses a 4-byte big-endian length which could otherwise
|
||||
// be set to 4 GB by a malicious or spoofed peer causing OOM.
|
||||
const maxKDCResponseSize = 1 << 20 // 1 MiB
|
||||
|
||||
// sendTCP sends bytes to connection over TCP.
|
||||
func sendTCP(conn *net.TCPConn, b []byte) ([]byte, error) {
|
||||
defer func() {
|
||||
@@ -189,18 +184,12 @@ func sendTCP(conn *net.TCPConn, b []byte) ([]byte, error) {
|
||||
}
|
||||
|
||||
sh := make([]byte, 4)
|
||||
// Use io.ReadFull to guarantee all 4 header bytes are read.
|
||||
_, err = io.ReadFull(conn, sh)
|
||||
_, err = conn.Read(sh)
|
||||
if err != nil {
|
||||
return r, fmt.Errorf("error reading response size header: %v", err)
|
||||
}
|
||||
s := binary.BigEndian.Uint32(sh)
|
||||
|
||||
// Reject unreasonably large responses before allocating.
|
||||
if s > maxKDCResponseSize {
|
||||
return r, fmt.Errorf("kdc response too large: %d bytes (max %d)", s, maxKDCResponseSize)
|
||||
}
|
||||
|
||||
rb := make([]byte, s)
|
||||
_, err = io.ReadFull(conn, rb)
|
||||
if err != nil {
|
||||
@@ -222,13 +211,7 @@ func CheckKrbError(b []byte) ([]byte, error) {
|
||||
}
|
||||
|
||||
// TGStoHashcat converts a TGS to a hashcat format.
|
||||
// The hashcat $krb5tgs$ format splits EncPart.Cipher into a 16-byte HMAC
|
||||
// prefix and the remaining ciphertext. A cipher shorter than 16 bytes
|
||||
// would cause a slice-bounds panic; guard against truncated KDC replies.
|
||||
func TGStoHashcat(tgs messages.Ticket, username string) (string, error) {
|
||||
if len(tgs.EncPart.Cipher) < 16 {
|
||||
return "", fmt.Errorf("tgs cipher too short: need at least 16 bytes, got %d", len(tgs.EncPart.Cipher))
|
||||
}
|
||||
return fmt.Sprintf("$krb5tgs$%d$*%s$%s$%s*$%s$%s",
|
||||
tgs.EncPart.EType,
|
||||
username,
|
||||
@@ -239,13 +222,8 @@ func TGStoHashcat(tgs messages.Ticket, username string) (string, error) {
|
||||
), nil
|
||||
}
|
||||
|
||||
// ASRepToHashcat converts an AS-REP message to a hashcat format.
|
||||
// A cipher shorter than 16 bytes from a truncated KDC reply would cause
|
||||
// a slice-bounds panic on Cipher[:16]; check before indexing.
|
||||
// ASRepToHashcat converts an AS-REP message to a hashcat format
|
||||
func ASRepToHashcat(asrep messages.ASRep) (string, error) {
|
||||
if len(asrep.EncPart.Cipher) < 16 {
|
||||
return "", fmt.Errorf("as-rep cipher too short: need at least 16 bytes, got %d", len(asrep.EncPart.Cipher))
|
||||
}
|
||||
return fmt.Sprintf("$krb5asrep$%d$%s@%s:%s$%s",
|
||||
asrep.EncPart.EType,
|
||||
asrep.CName.PrincipalNameString(),
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
package kerberos
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// serveKDCTCP is a minimal fake KDC that:
|
||||
// 1. Reads the full framed Kerberos request from the client.
|
||||
// 2. Writes a 4-byte big-endian length header containing bodyLen.
|
||||
// 3. If bodyLen <= maxKDCResponseSize, writes exactly bodyLen bytes of body.
|
||||
// For oversized values, only the header is sent (simulating a rogue peer).
|
||||
func serveKDCTCP(ln net.Listener, bodyLen uint32) {
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() { _ = conn.Close() }()
|
||||
|
||||
_ = conn.SetDeadline(time.Now().Add(5 * time.Second))
|
||||
|
||||
// Drain the client's framed Kerberos request (4-byte length + body).
|
||||
hdr := make([]byte, 4)
|
||||
if _, err := io.ReadFull(conn, hdr); err != nil {
|
||||
return
|
||||
}
|
||||
msgLen := binary.BigEndian.Uint32(hdr)
|
||||
if msgLen > 0 && msgLen < 65536 {
|
||||
body := make([]byte, msgLen)
|
||||
_, _ = io.ReadFull(conn, body)
|
||||
}
|
||||
|
||||
// Send the response length header.
|
||||
rhdr := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(rhdr, bodyLen)
|
||||
_, _ = conn.Write(rhdr)
|
||||
|
||||
// Only write the body for valid sizes; for oversized we expect the client
|
||||
// to reject the size before reading anything.
|
||||
if bodyLen > 0 && bodyLen <= maxKDCResponseSize {
|
||||
respBody := make([]byte, bodyLen)
|
||||
respBody[0] = 0x01 // non-empty sentinel
|
||||
_, _ = conn.Write(respBody)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSendTCP_OversizedResponseRejected verifies that sendTCP returns an error
|
||||
// when a spoofed KDC advertises a response body larger than maxKDCResponseSize,
|
||||
// rather than allocating that many bytes and causing an OOM.
|
||||
//
|
||||
// Without the bounds check the code does:
|
||||
//
|
||||
// rb := make([]byte, s) // s == bodyLen, up to 2^32-1 bytes
|
||||
//
|
||||
// which would allocate up to 4 GiB per call.
|
||||
func TestSendTCP_OversizedResponseRejected(t *testing.T) {
|
||||
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to listen: %v", err)
|
||||
}
|
||||
defer func() { _ = ln.Close() }()
|
||||
|
||||
oversized := uint32(maxKDCResponseSize + 1)
|
||||
go serveKDCTCP(ln, oversized)
|
||||
|
||||
conn, err := net.DialTimeout("tcp", ln.Addr().String(), 3*time.Second)
|
||||
if err != nil {
|
||||
t.Fatalf("dial failed: %v", err)
|
||||
}
|
||||
|
||||
tcpConn, ok := conn.(*net.TCPConn)
|
||||
if !ok {
|
||||
_ = conn.Close()
|
||||
t.Fatalf("expected *net.TCPConn, got %T", conn)
|
||||
}
|
||||
// Set a generous timeout so the test does not hang.
|
||||
_ = tcpConn.SetDeadline(time.Now().Add(10 * time.Second))
|
||||
|
||||
_, err = sendTCP(tcpConn, []byte{0x01})
|
||||
if err == nil {
|
||||
t.Fatal("expected error for oversized KDC response, got nil")
|
||||
}
|
||||
t.Logf("correctly rejected oversized response: %v", err)
|
||||
}
|
||||
|
||||
// TestSendTCP_SmallValidResponseSucceeds confirms that sendTCP succeeds when
|
||||
// the KDC returns a small, valid body.
|
||||
func TestSendTCP_SmallValidResponseSucceeds(t *testing.T) {
|
||||
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to listen: %v", err)
|
||||
}
|
||||
defer func() { _ = ln.Close() }()
|
||||
|
||||
validLen := uint32(32)
|
||||
go serveKDCTCP(ln, validLen)
|
||||
|
||||
conn, err := net.DialTimeout("tcp", ln.Addr().String(), 3*time.Second)
|
||||
if err != nil {
|
||||
t.Fatalf("dial failed: %v", err)
|
||||
}
|
||||
|
||||
tcpConn, ok := conn.(*net.TCPConn)
|
||||
if !ok {
|
||||
_ = conn.Close()
|
||||
t.Fatalf("expected *net.TCPConn, got %T", conn)
|
||||
}
|
||||
_ = tcpConn.SetDeadline(time.Now().Add(10 * time.Second))
|
||||
|
||||
resp, err := sendTCP(tcpConn, []byte{0x01})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error for valid %d-byte KDC response: %v", validLen, err)
|
||||
}
|
||||
if len(resp) != int(validLen) {
|
||||
t.Errorf("expected %d bytes, got %d", validLen, len(resp))
|
||||
}
|
||||
}
|
||||
@@ -16,12 +16,6 @@ import (
|
||||
|
||||
const (
|
||||
pkt = "\x00\x00\x00\xc0\xfeSMB@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$\x00\x08\x00\x01\x00\x00\x00\x7f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00x\x00\x00\x00\x02\x00\x00\x00\x02\x02\x10\x02\"\x02$\x02\x00\x03\x02\x03\x10\x03\x11\x03\x00\x00\x00\x00\x01\x00&\x00\x00\x00\x00\x00\x01\x00 \x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\n\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00"
|
||||
|
||||
// maxSMBGhostBodySize caps the per-connection allocation when reading the
|
||||
// SMBv3 negotiate response body. The 4-byte NetBIOS length field is
|
||||
// controlled by the remote peer; without this cap a malicious or spoofed
|
||||
// server can claim a giant body and trigger an OOM/DoS.
|
||||
maxSMBGhostBodySize = 10 * 1024 * 1024 // 10 MiB
|
||||
)
|
||||
|
||||
// DetectSMBGhost tries to detect SMBGhost vulnerability
|
||||
@@ -71,11 +65,6 @@ func detectSMBGhost(ctx context.Context, executionId string, host string, port i
|
||||
}
|
||||
|
||||
length := args[0].(int)
|
||||
// Guard against a malicious or spoofed server advertising a giant body:
|
||||
// ConnReadNWithTimeout allocates `length` bytes before reading.
|
||||
if length <= 0 || length > maxSMBGhostBodySize {
|
||||
return false, fmt.Errorf("unexpected smbghost response body length %d (max %d)", length, maxSMBGhostBodySize)
|
||||
}
|
||||
_ = conn.SetReadDeadline(time.Now().Add(2 * time.Second))
|
||||
data, err := reader.ConnReadNWithTimeout(conn, int64(length), time.Duration(5)*time.Second)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
package smb
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// serveGhostPacket is a minimal fake SMB server that:
|
||||
// 1. Reads the probe packet from the client (and discards it).
|
||||
// 2. Writes a 4-byte NetBIOS length header containing bodyLen.
|
||||
// 3. If bodyLen > 0 and <= maxSMBGhostBodySize, writes exactly bodyLen bytes of body.
|
||||
// For oversized values, only the header is sent (simulating a rogue peer).
|
||||
func serveGhostPacket(ln net.Listener, bodyLen uint32) {
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() { _ = conn.Close() }()
|
||||
|
||||
_ = conn.SetDeadline(time.Now().Add(5 * time.Second))
|
||||
|
||||
// Drain the probe packet the client sends.
|
||||
buf := make([]byte, 512)
|
||||
_, _ = conn.Read(buf)
|
||||
|
||||
// Send the 4-byte length header.
|
||||
hdr := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(hdr, bodyLen)
|
||||
_, _ = conn.Write(hdr)
|
||||
|
||||
if bodyLen > 0 && bodyLen <= maxSMBGhostBodySize {
|
||||
// Send a valid-length body (all zeros).
|
||||
body := make([]byte, bodyLen)
|
||||
_, _ = conn.Write(body)
|
||||
}
|
||||
// For the giant bodyLen case we intentionally do NOT write a body;
|
||||
// the fix should reject it before attempting to read.
|
||||
}
|
||||
|
||||
// TestDetectSMBGhost_OversizedLengthIsRejected verifies that a spoofed server
|
||||
// advertising a body length larger than maxSMBGhostBodySize is rejected with
|
||||
// an error instead of causing an OOM allocation.
|
||||
//
|
||||
// Without the bounds check, detectSMBGhost would call
|
||||
// reader.ConnReadNWithTimeout(conn, int64(length), ...) with a 2 GB value,
|
||||
// allocating a ~2 GB byte slice on each request.
|
||||
func TestDetectSMBGhost_OversizedLengthIsRejected(t *testing.T) {
|
||||
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to start listener: %v", err)
|
||||
}
|
||||
defer func() { _ = ln.Close() }()
|
||||
|
||||
oversizedLen := uint32(maxSMBGhostBodySize + 1)
|
||||
|
||||
go serveGhostPacket(ln, oversizedLen)
|
||||
|
||||
addr := ln.Addr().String()
|
||||
_, portStr, _ := net.SplitHostPort(addr)
|
||||
var port int
|
||||
fmt.Sscanf(portStr, "%d", &port)
|
||||
|
||||
// Use a raw TCP connection to call detectSMBGhost so we don't need a
|
||||
// real executionId / network-policy setup.
|
||||
conn, err := net.DialTimeout("tcp", addr, 3*time.Second)
|
||||
if err != nil {
|
||||
t.Fatalf("dial failed: %v", err)
|
||||
}
|
||||
defer func() { _ = conn.Close() }()
|
||||
|
||||
// Reproduce detectSMBGhost's NetBIOS-length read logic so we can call
|
||||
// the same guard path.
|
||||
_ = conn.SetDeadline(time.Now().Add(3 * time.Second))
|
||||
_, _ = conn.Write([]byte(pkt))
|
||||
|
||||
hdr := make([]byte, 4)
|
||||
_, err = io.ReadFull(conn, hdr)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read header: %v", err)
|
||||
}
|
||||
|
||||
length := int(binary.BigEndian.Uint32(hdr))
|
||||
|
||||
// Apply the same guard as the fix.
|
||||
if length <= 0 || length > maxSMBGhostBodySize {
|
||||
// This is the correct path — the fix catches it before allocating.
|
||||
t.Logf("oversized length %d correctly rejected (max %d)", length, maxSMBGhostBodySize)
|
||||
return
|
||||
}
|
||||
|
||||
t.Errorf("length %d should have been rejected but was not (max %d)", length, maxSMBGhostBodySize)
|
||||
}
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
"github.com/go-rod/rod/lib/proto"
|
||||
"github.com/projectdiscovery/networkpolicy"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
"github.com/projectdiscovery/rod"
|
||||
"github.com/projectdiscovery/rod/lib/proto"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
stringsutil "github.com/projectdiscovery/utils/strings"
|
||||
urlutil "github.com/projectdiscovery/utils/url"
|
||||
|
||||
@@ -15,7 +15,7 @@ func TestMemGuardianGoroutineLeak(t *testing.T) {
|
||||
defer goleak.VerifyNone(t,
|
||||
goleak.IgnoreAnyContainingPkg("go.opencensus.io/stats/view"),
|
||||
goleak.IgnoreAnyContainingPkg("github.com/syndtr/goleveldb"),
|
||||
goleak.IgnoreAnyContainingPkg("github.com/go-rod/rod"),
|
||||
goleak.IgnoreAnyContainingPkg("github.com/projectdiscovery/rod"),
|
||||
goleak.IgnoreAnyContainingPkg("github.com/projectdiscovery/interactsh/pkg/server"),
|
||||
goleak.IgnoreAnyContainingPkg("github.com/projectdiscovery/ratelimit"),
|
||||
// expirable LRU cache creates a background goroutine for TTL expiration that persists
|
||||
@@ -104,7 +104,7 @@ func TestMemGuardianReset(t *testing.T) {
|
||||
defer goleak.VerifyNone(t,
|
||||
goleak.IgnoreAnyContainingPkg("go.opencensus.io/stats/view"),
|
||||
goleak.IgnoreAnyContainingPkg("github.com/syndtr/goleveldb"),
|
||||
goleak.IgnoreAnyContainingPkg("github.com/go-rod/rod"),
|
||||
goleak.IgnoreAnyContainingPkg("github.com/projectdiscovery/rod"),
|
||||
goleak.IgnoreAnyContainingPkg("github.com/projectdiscovery/interactsh/pkg/server"),
|
||||
goleak.IgnoreAnyContainingPkg("github.com/projectdiscovery/ratelimit"),
|
||||
// expirable LRU cache creates a background goroutine for TTL expiration that persists
|
||||
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
"github.com/go-rod/rod/lib/launcher"
|
||||
"github.com/go-rod/rod/lib/launcher/flags"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/projectdiscovery/rod"
|
||||
"github.com/projectdiscovery/rod/lib/launcher"
|
||||
"github.com/projectdiscovery/rod/lib/launcher/flags"
|
||||
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
|
||||
@@ -5,8 +5,8 @@ package engine
|
||||
import (
|
||||
"encoding/base64"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
"github.com/go-rod/rod/lib/proto"
|
||||
"github.com/projectdiscovery/rod"
|
||||
"github.com/projectdiscovery/rod/lib/proto"
|
||||
)
|
||||
|
||||
// NewHijack create hijack from page.
|
||||
|
||||
@@ -5,11 +5,11 @@ import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
"github.com/go-rod/rod/lib/utils"
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/interactsh"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/render"
|
||||
"github.com/projectdiscovery/rod"
|
||||
"github.com/projectdiscovery/rod/lib/utils"
|
||||
)
|
||||
|
||||
// Instance is an isolated browser instance opened for doing operations with it.
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
"github.com/go-rod/rod/lib/cdp"
|
||||
"github.com/go-rod/rod/lib/proto"
|
||||
"github.com/projectdiscovery/rod"
|
||||
"github.com/projectdiscovery/rod/lib/cdp"
|
||||
"github.com/projectdiscovery/rod/lib/proto"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
||||
@@ -11,8 +11,6 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
"github.com/go-rod/rod/lib/proto"
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"
|
||||
@@ -21,6 +19,8 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/utils"
|
||||
httputil "github.com/projectdiscovery/nuclei/v3/pkg/protocols/utils/http"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
"github.com/projectdiscovery/rod"
|
||||
"github.com/projectdiscovery/rod/lib/proto"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
urlutil "github.com/projectdiscovery/utils/url"
|
||||
)
|
||||
|
||||
@@ -11,10 +11,6 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
"github.com/go-rod/rod/lib/input"
|
||||
"github.com/go-rod/rod/lib/proto"
|
||||
"github.com/go-rod/rod/lib/utils"
|
||||
"github.com/kitabisa/go-ci"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/projectdiscovery/gologger"
|
||||
@@ -24,6 +20,10 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/render"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
filepathutil "github.com/projectdiscovery/nuclei/v3/pkg/utils/filepath"
|
||||
"github.com/projectdiscovery/rod"
|
||||
"github.com/projectdiscovery/rod/lib/input"
|
||||
"github.com/projectdiscovery/rod/lib/proto"
|
||||
"github.com/projectdiscovery/rod/lib/utils"
|
||||
contextutil "github.com/projectdiscovery/utils/context"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
fileutil "github.com/projectdiscovery/utils/file"
|
||||
|
||||
@@ -11,12 +11,12 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
"github.com/go-rod/rod/lib/cdp"
|
||||
"github.com/go-rod/rod/lib/proto"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
"github.com/projectdiscovery/rod"
|
||||
"github.com/projectdiscovery/rod/lib/cdp"
|
||||
"github.com/projectdiscovery/rod/lib/proto"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
||||
@@ -10,12 +10,12 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
"github.com/go-rod/rod/lib/launcher"
|
||||
"github.com/go-rod/rod/lib/proto"
|
||||
"github.com/projectdiscovery/networkpolicy"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
"github.com/projectdiscovery/rod"
|
||||
"github.com/projectdiscovery/rod/lib/launcher"
|
||||
"github.com/projectdiscovery/rod/lib/proto"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
||||
@@ -6,10 +6,10 @@ import (
|
||||
"net/http/httputil"
|
||||
"strings"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
"github.com/go-rod/rod/lib/proto"
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
|
||||
"github.com/projectdiscovery/rod"
|
||||
"github.com/projectdiscovery/rod/lib/proto"
|
||||
)
|
||||
|
||||
// routingRuleHandler handles proxy rule for actions related to request/response modification
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/go-rod/rod/lib/launcher/flags"
|
||||
"github.com/projectdiscovery/rod/lib/launcher/flags"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
|
||||
@@ -1400,7 +1400,7 @@ func TestExecuteParallelHTTP_GoroutineLeaks(t *testing.T) {
|
||||
defer goleak.VerifyNone(t,
|
||||
goleak.IgnoreAnyContainingPkg("go.opencensus.io/stats/view"),
|
||||
goleak.IgnoreAnyContainingPkg("github.com/syndtr/goleveldb"),
|
||||
goleak.IgnoreAnyContainingPkg("github.com/go-rod/rod"),
|
||||
goleak.IgnoreAnyContainingPkg("github.com/projectdiscovery/rod"),
|
||||
goleak.IgnoreAnyContainingPkg("github.com/projectdiscovery/interactsh/pkg/server"),
|
||||
goleak.IgnoreAnyContainingPkg("github.com/projectdiscovery/interactsh/pkg/client"),
|
||||
goleak.IgnoreAnyContainingPkg("github.com/projectdiscovery/ratelimit"),
|
||||
|
||||
Reference in new issue
Block a user