Path: blob/dev/pkg/js/libs/kerberos/sendtokdc.go
2070 views
package kerberos12// the following code is adapted from the original library3// https://github.com/jcmturner/gokrb5/blob/855dbc707a37a21467aef6c0245fcf3328dc39ed/v8/client/network.go4// it is copied here because the library does not export "SendToKDC()"56import (7"context"8"encoding/binary"9"encoding/hex"10"fmt"11"io"12"net"13"strings"14"time"1516"github.com/jcmturner/gokrb5/v8/messages"17"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"18)1920// sendtokdc.go deals with actual sending and receiving responses from KDC21// SendToKDC sends a message to the KDC and returns the response.22// It first tries to send the message over TCP, and if that fails, it falls back to UDP.(and vice versa)23// @example24// ```javascript25// const kerberos = require('nuclei/kerberos');26// const client = new kerberos.Client('acme.com');27// const response = kerberos.SendToKDC(client, 'message');28// ```29func SendToKDC(kclient *Client, msg string) (string, error) {30if kclient == nil || kclient.nj == nil || kclient.Krb5Config == nil || kclient.Realm == "" {31return "", fmt.Errorf("kerberos client is not initialized")32}33if kclient.config.timeout == 0 {34kclient.config.timeout = 5 // default timeout 5 seconds35}36var response []byte37var err error3839response, err = sendToKDCTcp(kclient, msg)40if err == nil {41// if it related to tcp42bin, err := CheckKrbError(response)43if err == nil {44return string(bin), nil45}46// if it is krb error no need to do udp47if e, ok := err.(messages.KRBError); ok {48return string(response), e49}50}5152// fallback to udp53response, err = sendToKDCUdp(kclient, msg)54if err == nil {55// if it related to udp56bin, err := CheckKrbError(response)57if err == nil {58return string(bin), nil59}60}61return string(response), err62}6364// sendToKDCTcp sends a message to the KDC via TCP.65func sendToKDCTcp(kclient *Client, msg string) ([]byte, error) {66_, kdcs, err := kclient.Krb5Config.GetKDCs(kclient.Realm, true)67kclient.nj.HandleError(err, "error getting KDCs")68kclient.nj.Require(len(kdcs) > 0, "no KDCs found")6970executionId := kclient.nj.ExecutionId()71dialers := protocolstate.GetDialersWithId(executionId)72if dialers == nil {73return nil, fmt.Errorf("dialers not initialized for %s", executionId)74}7576var errs []string77for i := 1; i <= len(kdcs); i++ {78host, port, err := net.SplitHostPort(kdcs[i])79if err == nil && kclient.config.ip != "" {80// use that ip address instead of realm/domain for resolving81host = kclient.config.ip82}83tcpConn, err := dialers.Fastdialer.Dial(context.TODO(), "tcp", net.JoinHostPort(host, port))84if err != nil {85errs = append(errs, fmt.Sprintf("error establishing connection to %s: %v", kdcs[i], err))86continue87}88defer func() {89_ = tcpConn.Close()90}()91_ = tcpConn.SetDeadline(time.Now().Add(time.Duration(kclient.config.timeout) * time.Second)) //read and write deadline92rb, err := sendTCP(tcpConn.(*net.TCPConn), []byte(msg))93if err != nil {94errs = append(errs, fmt.Sprintf("error sending to %s: %v", kdcs[i], err))95continue96}97return rb, nil98}99if len(errs) > 0 {100return nil, fmt.Errorf("error sending to a KDC: %s", strings.Join(errs, "; "))101}102return nil, nil103}104105// sendToKDCUdp sends a message to the KDC via UDP.106func sendToKDCUdp(kclient *Client, msg string) ([]byte, error) {107_, kdcs, err := kclient.Krb5Config.GetKDCs(kclient.Realm, true)108kclient.nj.HandleError(err, "error getting KDCs")109kclient.nj.Require(len(kdcs) > 0, "no KDCs found")110111executionId := kclient.nj.ExecutionId()112dialers := protocolstate.GetDialersWithId(executionId)113if dialers == nil {114return nil, fmt.Errorf("dialers not initialized for %s", executionId)115}116var errs []string117for i := 1; i <= len(kdcs); i++ {118host, port, err := net.SplitHostPort(kdcs[i])119if err == nil && kclient.config.ip != "" {120// use that ip address instead of realm/domain for resolving121host = kclient.config.ip122}123udpConn, err := dialers.Fastdialer.Dial(context.TODO(), "udp", net.JoinHostPort(host, port))124if err != nil {125errs = append(errs, fmt.Sprintf("error establishing connection to %s: %v", kdcs[i], err))126continue127}128defer func() {129_ = udpConn.Close()130}()131_ = udpConn.SetDeadline(time.Now().Add(time.Duration(kclient.config.timeout) * time.Second)) //read and write deadline132rb, err := sendUDP(udpConn.(*net.UDPConn), []byte(msg))133if err != nil {134errs = append(errs, fmt.Sprintf("error sending to %s: %v", kdcs[i], err))135continue136}137return rb, nil138}139if len(errs) > 0 {140// fallback to tcp141return nil, fmt.Errorf("error sending to a KDC: %s", strings.Join(errs, "; "))142}143return nil, nil144}145146// sendUDP sends bytes to connection over UDP.147func sendUDP(conn *net.UDPConn, b []byte) ([]byte, error) {148var r []byte149defer func() {150_ = conn.Close()151}()152_, err := conn.Write(b)153if err != nil {154return r, fmt.Errorf("error sending to (%s): %v", conn.RemoteAddr().String(), err)155}156udpbuf := make([]byte, 4096)157n, _, err := conn.ReadFrom(udpbuf)158r = udpbuf[:n]159if err != nil {160return r, fmt.Errorf("sending over UDP failed to %s: %v", conn.RemoteAddr().String(), err)161}162if len(r) < 1 {163return r, fmt.Errorf("no response data from %s", conn.RemoteAddr().String())164}165return r, nil166}167168// sendTCP sends bytes to connection over TCP.169func sendTCP(conn *net.TCPConn, b []byte) ([]byte, error) {170defer func() {171_ = conn.Close()172}()173var r []byte174// RFC 4120 7.2.2 specifies the first 4 bytes indicate the length of the message in big endian order.175hb := make([]byte, 4)176binary.BigEndian.PutUint32(hb, uint32(len(b)))177b = append(hb, b...)178179_, err := conn.Write(b)180if err != nil {181return r, fmt.Errorf("error sending to KDC (%s): %v", conn.RemoteAddr().String(), err)182}183184sh := make([]byte, 4)185_, err = conn.Read(sh)186if err != nil {187return r, fmt.Errorf("error reading response size header: %v", err)188}189s := binary.BigEndian.Uint32(sh)190191rb := make([]byte, s)192_, err = io.ReadFull(conn, rb)193if err != nil {194return r, fmt.Errorf("error reading response: %v", err)195}196if len(rb) < 1 {197return r, fmt.Errorf("no response data from KDC %s", conn.RemoteAddr().String())198}199return rb, nil200}201202// CheckKrbError checks if the response bytes from the KDC are a KRBError.203func CheckKrbError(b []byte) ([]byte, error) {204var KRBErr messages.KRBError205if err := KRBErr.Unmarshal(b); err == nil {206return b, KRBErr207}208return b, nil209}210211// TGStoHashcat converts a TGS to a hashcat format.212func TGStoHashcat(tgs messages.Ticket, username string) (string, error) {213return fmt.Sprintf("$krb5tgs$%d$*%s$%s$%s*$%s$%s",214tgs.EncPart.EType,215username,216tgs.Realm,217strings.Join(tgs.SName.NameString[:], "/"),218hex.EncodeToString(tgs.EncPart.Cipher[:16]),219hex.EncodeToString(tgs.EncPart.Cipher[16:]),220), nil221}222223// ASRepToHashcat converts an AS-REP message to a hashcat format224func ASRepToHashcat(asrep messages.ASRep) (string, error) {225return fmt.Sprintf("$krb5asrep$%d$%s@%s:%s$%s",226asrep.EncPart.EType,227asrep.CName.PrincipalNameString(),228asrep.CRealm,229hex.EncodeToString(asrep.EncPart.Cipher[:16]),230hex.EncodeToString(asrep.EncPart.Cipher[16:])), nil231}232233234