From e6ecf3be1049641ad280bd8861e49668ffb3c930 Mon Sep 17 00:00:00 2001 From: NielsAD Date: Mon, 25 Mar 2019 15:15:41 +0100 Subject: [PATCH] bnet: Add support for SHA1 pw auth (used in old PvPGN servers) --- cmd/bncsclient/README.md | 14 +++++++++ cmd/bncsclient/main.go | 11 ++++--- network/bnet/bncsutil.go | 62 +++++++++++++++++++++++++++++++++----- network/bnet/bnet.go | 64 ++++++++++++++++++++++------------------ protocol/bncs/packets.go | 7 ++++- 5 files changed, 118 insertions(+), 40 deletions(-) diff --git a/cmd/bncsclient/README.md b/cmd/bncsclient/README.md index 54529ef..ee0f6a3 100644 --- a/cmd/bncsclient/README.md +++ b/cmd/bncsclient/README.md @@ -23,12 +23,15 @@ Usage |`-roc` |`string`|ROC CD-key| |`-tft` |`string`|TFT CD-key| |`-verify` |`bool` |Verify server signature| +|`-sha1` |`bool` |SHA1 password authentication (used in old PvPGN servers)| |`-create` |`bool` |Create account| |`-changepass`|`bool` |Change password| Example ------- +Loading version and CD-key info from the default Warcraft III installation directory: + ```bash ➜ ./bncsclient -u niels europe.battle.net Enter password: @@ -42,6 +45,17 @@ Enter password: 12:00:05 [INFO] No one hears you. ``` +Preset version and CD-key info, SHA1 (PvPGN) password authentication: + +```bash +➜ ./bncsclient -sha1 -u niels -roc FFFFFFFFFFFFFFFFFFFFFFFFFF -tft FFFFFFFFFFFFFFFFFFFFFFFFFF -ev 0x011b01ad -eh 0xaaaba048 rubattle.net +12:00:00 Succesfully logged onto niels@rubattle.net:6112 +12:00:00 Joined channel 'Warcraft III RUS-1' +12:00:00 niels has joined the channel (ping: 41ms) +12:00:00 [INFO] Obey the law, rules are the law! +12:00:00 [INFO] Hello niels, welcome to Rubattle.net! +``` + Download -------- diff --git a/cmd/bncsclient/main.go b/cmd/bncsclient/main.go index 771cf85..2541ea0 100644 --- a/cmd/bncsclient/main.go +++ b/cmd/bncsclient/main.go @@ -34,6 +34,7 @@ var ( password = flag.String("p", "", "Password") newpassword = flag.String("np", "", "New password") verify = flag.Bool("verify", false, "Verify server signature") + sha1 = flag.Bool("sha1", false, "SHA1 password authentication (used in old PvPGN servers)") create = flag.Bool("create", false, "Create account") changepass = flag.Bool("changepass", false, "Change password") ) @@ -46,10 +47,12 @@ func main() { flag.Parse() c, err := bnet.NewClient(&bnet.Config{ - BinPath: *binpath, - ExeInfo: *exeinfo, - ExeVersion: uint32(*exevers), - ExeHash: uint32(*exehash), + BinPath: *binpath, + ExeInfo: *exeinfo, + ExeVersion: uint32(*exevers), + ExeHash: uint32(*exehash), + VerifySignature: *verify, + SHA1Auth: *sha1, }) if err != nil { logErr.Fatal("NewClient error: ", err) diff --git a/network/bnet/bncsutil.go b/network/bnet/bncsutil.go index aa0a14e..a152e79 100644 --- a/network/bnet/bncsutil.go +++ b/network/bnet/bncsutil.go @@ -88,18 +88,27 @@ func CreateBNCSKeyInfo(cdkey string, clientToken uint32, serverToken uint32) (*b }, nil } -// VerifyNLSSignature received in SID_AUTH_INFO (0x50) -func VerifyNLSSignature(ip net.IP, sig *[128]byte) bool { +// VerifyServerSignature received in SID_AUTH_INFO (0x50) +func VerifyServerSignature(ip net.IP, sig *[128]byte) bool { var aton = binary.LittleEndian.Uint32(ip.To4()) return C.nls_check_signature(C.uint32_t(aton), (*C.char)(unsafe.Pointer(&sig[0]))) != 0 } -// NLS password hashing helper +// SRP password helper +type SRP interface { + AccountCreate() ([]byte, []byte, error) + ClientKey() [32]byte + PasswordProof(serverKey *[32]byte, salt *[32]byte) [20]byte + VerifyPassword(proof *[20]byte) bool + Free() +} + +// NLS provider for SRP type NLS struct { n *C.nls_t } -// NewNLS initializes a new NLS struct +// NewNLS initializes a new NLS provider for SRP func NewNLS(username string, password string) (*NLS, error) { var cstru = C.CString(username) defer C.free(unsafe.Pointer(cstru)) @@ -133,19 +142,58 @@ func (n *NLS) AccountCreate() ([]byte, []byte, error) { return buf[0:32], buf[32:64], nil } -// ClientKey for NLS exchange +// ClientKey for SRP exchange func (n *NLS) ClientKey() (res [32]byte) { C.nls_get_A(n.n, (*C.char)(unsafe.Pointer(&res[0]))) return res } -// PasswordProof for NLS exchange +// PasswordProof for SRP exchange func (n *NLS) PasswordProof(serverKey *[32]byte, salt *[32]byte) (res [20]byte) { C.nls_get_M1(n.n, (*C.char)(unsafe.Pointer(&res[0])), (*C.char)(unsafe.Pointer(&serverKey[0])), (*C.char)(unsafe.Pointer(&salt[0]))) return res } -// VerifyPassword after NLS exchange +// VerifyPassword after SRP exchange func (n *NLS) VerifyPassword(proof *[20]byte) bool { return C.nls_check_M2(n.n, (*C.char)(unsafe.Pointer(&proof[0])), nil, nil) != 0 } + +// SHA1 provider for SRP +type SHA1 struct { + password string +} + +// NewSHA1 initializes a new SHA1 provider for SRP +func NewSHA1(password string) *SHA1 { + return &SHA1{ + password: password, + } +} + +// Free SHA1 struct +func (p *SHA1) Free() {} + +// AccountCreate generates the content for an SID_AUTH_ACCOUNTCREATE packet +func (p *SHA1) AccountCreate() ([]byte, []byte, error) { + return nil, []byte(p.password), nil +} + +// ClientKey for SRP exchange +func (p *SHA1) ClientKey() (res [32]byte) { + return res +} + +// PasswordProof for SRP exchange +func (p *SHA1) PasswordProof(serverKey *[32]byte, salt *[32]byte) (res [20]byte) { + var cstrp = C.CString(p.password) + defer C.free(unsafe.Pointer(cstrp)) + + C.hashPassword(cstrp, (*C.char)(unsafe.Pointer(&res[0]))) + return res +} + +// VerifyPassword after SRP exchange +func (p *SHA1) VerifyPassword(proof *[20]byte) bool { + return true +} diff --git a/network/bnet/bnet.go b/network/bnet/bnet.go index c5184c8..a9b995e 100644 --- a/network/bnet/bnet.go +++ b/network/bnet/bnet.go @@ -33,6 +33,7 @@ type Config struct { ExeVersion uint32 ExeHash uint32 VerifySignature bool + SHA1Auth bool Username string Password string CDKeyOwner string @@ -237,7 +238,7 @@ func (b *Client) Dial() (*network.BNCSConn, error) { return nil, err } - if b.VerifySignature && !VerifyNLSSignature(conn.RemoteAddr().(*net.TCPAddr).IP, &authInfo.ServerSignature) { + if b.VerifySignature && !VerifyServerSignature(conn.RemoteAddr().(*net.TCPAddr).IP, &authInfo.ServerSignature) { bncsconn.Close() return nil, ErrInvalidServerSig } @@ -261,7 +262,7 @@ func (b *Client) Dial() (*network.BNCSConn, error) { // // Logon sequence: // 1. Client starts with Dial sequence ([0x50] SID_AUTH_INFO and [0x51] SID_AUTH_CHECK) -// 2. Client waits for user to enter account information (standard logon shown, uses NLS): +// 2. Client waits for user to enter account information (standard logon shown, uses SRP): // 1. C > S [0x53] SID_AUTH_ACCOUNTLOGON // 2. S > C [0x53] SID_AUTH_ACCOUNTLOGON // 3. C > S [0x54] SID_AUTH_ACCOUNTLOGONPROOF @@ -279,19 +280,19 @@ func (b *Client) Dial() (*network.BNCSConn, error) { // 13. A sequence of chat events for entering chat follow. // func (b *Client) Logon() error { - nls, err := NewNLS(b.Username, b.Password) + srp, err := b.newSRP(b.Password) if err != nil { return err } - defer nls.Free() + defer srp.Free() bncsconn, err := b.Dial() if err != nil { return err } - logon, err := b.sendLogon(bncsconn, nls) + logon, err := b.sendLogon(bncsconn, srp) if err != nil { bncsconn.Close() return err @@ -302,7 +303,7 @@ func (b *Client) Logon() error { return LogonResultToError(logon.Result) } - proof, err := b.sendLogonProof(bncsconn, nls, logon) + proof, err := b.sendLogonProof(bncsconn, srp, logon) if err != nil { bncsconn.Close() return err @@ -321,7 +322,7 @@ func (b *Client) Logon() error { return LogonProofResultToError(proof.Result) } - if !nls.VerifyPassword(&proof.ServerPasswordProof) { + if !srp.VerifyPassword(&proof.ServerPasswordProof) { bncsconn.Close() return ErrPasswordVerification } @@ -352,12 +353,12 @@ func (b *Client) Logon() error { // 3. Client can continue with logon ([0x53] SID_AUTH_ACCOUNTLOGON) // func (b *Client) CreateAccount() error { - nls, err := NewNLS(b.Username, b.Password) + srp, err := b.newSRP(b.Password) if err != nil { return err } - defer nls.Free() + defer srp.Free() bncsconn, err := b.Dial() if err != nil { @@ -366,7 +367,7 @@ func (b *Client) CreateAccount() error { defer bncsconn.Close() - create, err := b.sendCreateAccount(bncsconn, nls) + create, err := b.sendCreateAccount(bncsconn, srp) if err != nil { return err } @@ -390,19 +391,19 @@ func (b *Client) CreateAccount() error { // 3. Client can continue with logon ([0x53] SID_AUTH_ACCOUNTLOGON) // func (b *Client) ChangePassword(newPassword string) error { - oldNLS, err := NewNLS(b.Username, b.Password) + oldSRP, err := b.newSRP(b.Password) if err != nil { return err } - defer oldNLS.Free() + defer oldSRP.Free() - newNLS, err := NewNLS(b.Username, newPassword) + newSRP, err := b.newSRP(newPassword) if err != nil { return err } - defer newNLS.Free() + defer newSRP.Free() bncsconn, err := b.Dial() if err != nil { @@ -411,7 +412,7 @@ func (b *Client) ChangePassword(newPassword string) error { defer bncsconn.Close() - resp, err := b.sendChangePass(bncsconn, oldNLS) + resp, err := b.sendChangePass(bncsconn, oldSRP) if err != nil { return err } @@ -420,7 +421,7 @@ func (b *Client) ChangePassword(newPassword string) error { return LogonResultToError(resp.Result) } - proof, err := b.sendChangePassProof(bncsconn, oldNLS, newNLS, resp) + proof, err := b.sendChangePassProof(bncsconn, oldSRP, newSRP, resp) if err != nil { return err } @@ -429,7 +430,7 @@ func (b *Client) ChangePassword(newPassword string) error { return LogonProofResultToError(proof.Result) } - if !oldNLS.VerifyPassword(&proof.ServerPasswordProof) { + if !oldSRP.VerifyPassword(&proof.ServerPasswordProof) { return ErrPasswordVerification } @@ -437,6 +438,13 @@ func (b *Client) ChangePassword(newPassword string) error { return nil } +func (b *Client) newSRP(password string) (SRP, error) { + if b.SHA1Auth { + return NewSHA1(password), nil + } + return NewNLS(b.Username, password) +} + func (b *Client) sendAuthInfo(conn *network.BNCSConn) (*bncs.AuthInfoResp, error) { if _, err := conn.Send(&b.Platform); err != nil { return nil, err @@ -551,9 +559,9 @@ func (b *Client) sendAuthCheck(conn *network.BNCSConn, clientToken uint32, authi } } -func (b *Client) sendLogon(conn *network.BNCSConn, nls *NLS) (*bncs.AuthAccountLogonResp, error) { +func (b *Client) sendLogon(conn *network.BNCSConn, srp SRP) (*bncs.AuthAccountLogonResp, error) { var req = &bncs.AuthAccountLogonReq{ - ClientKey: nls.ClientKey(), + ClientKey: srp.ClientKey(), Username: b.Username, } @@ -573,9 +581,9 @@ func (b *Client) sendLogon(conn *network.BNCSConn, nls *NLS) (*bncs.AuthAccountL } } -func (b *Client) sendLogonProof(conn *network.BNCSConn, nls *NLS, logon *bncs.AuthAccountLogonResp) (*bncs.AuthAccountLogonProofResp, error) { +func (b *Client) sendLogonProof(conn *network.BNCSConn, srp SRP, logon *bncs.AuthAccountLogonResp) (*bncs.AuthAccountLogonProofResp, error) { var req = &bncs.AuthAccountLogonProofReq{ - ClientPasswordProof: nls.PasswordProof(&logon.ServerKey, &logon.Salt), + ClientPasswordProof: srp.PasswordProof(&logon.ServerKey, &logon.Salt), } if _, err := conn.Send(req); err != nil { @@ -594,8 +602,8 @@ func (b *Client) sendLogonProof(conn *network.BNCSConn, nls *NLS, logon *bncs.Au } } -func (b *Client) sendCreateAccount(conn *network.BNCSConn, nls *NLS) (*bncs.AuthAccountCreateResp, error) { - salt, verifier, err := nls.AccountCreate() +func (b *Client) sendCreateAccount(conn *network.BNCSConn, srp SRP) (*bncs.AuthAccountCreateResp, error) { + salt, verifier, err := srp.AccountCreate() if err != nil { return nil, err } @@ -620,10 +628,10 @@ func (b *Client) sendCreateAccount(conn *network.BNCSConn, nls *NLS) (*bncs.Auth } } -func (b *Client) sendChangePass(conn *network.BNCSConn, nls *NLS) (*bncs.AuthAccountChangePassResp, error) { +func (b *Client) sendChangePass(conn *network.BNCSConn, srp SRP) (*bncs.AuthAccountChangePassResp, error) { var req = &bncs.AuthAccountChangePassReq{ AuthAccountLogonReq: bncs.AuthAccountLogonReq{ - ClientKey: nls.ClientKey(), + ClientKey: srp.ClientKey(), Username: b.Username, }, } @@ -644,14 +652,14 @@ func (b *Client) sendChangePass(conn *network.BNCSConn, nls *NLS) (*bncs.AuthAcc } } -func (b *Client) sendChangePassProof(conn *network.BNCSConn, oldNLS *NLS, newNLS *NLS, resp *bncs.AuthAccountChangePassResp) (*bncs.AuthAccountChangePassProofResp, error) { - salt, verifier, err := newNLS.AccountCreate() +func (b *Client) sendChangePassProof(conn *network.BNCSConn, oldSRP SRP, newSRP SRP, resp *bncs.AuthAccountChangePassResp) (*bncs.AuthAccountChangePassProofResp, error) { + salt, verifier, err := newSRP.AccountCreate() if err != nil { return nil, err } var req = &bncs.AuthAccountChangePassProofReq{ - ClientPasswordProof: oldNLS.PasswordProof(&resp.ServerKey, &resp.Salt), + ClientPasswordProof: oldSRP.PasswordProof(&resp.ServerKey, &resp.Salt), } copy(req.NewSalt[:], salt) copy(req.NewVerifier[:], verifier) diff --git a/protocol/bncs/packets.go b/protocol/bncs/packets.go index 3399ff9..c0d53a8 100644 --- a/protocol/bncs/packets.go +++ b/protocol/bncs/packets.go @@ -434,6 +434,11 @@ func (pkt *ChatEvent) Serialize(buf *protocol.Buffer) error { return nil } +func baadf00d(n uint32) bool { + // PvPGN quirk + return n == 0xbaadf00d || n == 0x0df0adba +} + // Deserialize decodes the binary data generated by Serialize. func (pkt *ChatEvent) Deserialize(buf *protocol.Buffer) error { var size = readPacketSize(buf) @@ -451,7 +456,7 @@ func (pkt *ChatEvent) Deserialize(buf *protocol.Buffer) error { } pkt.Ping = buf.ReadUInt32() - if buf.ReadUInt32() != 0 || buf.ReadUInt32() != 0xbaadf00d || buf.ReadUInt32() != 0xbaadf00d { + if buf.ReadUInt32() != 0 || !baadf00d(buf.ReadUInt32()) || !baadf00d(buf.ReadUInt32()) { return ErrUnexpectedConst }