📜 ⬆️ ⬇️

VoiceAttack Security Study



Which eventually threw a few pleasant surprises. Carefully, under the cut there are a lot of screenshots in the highres and not the highres, which are not removed under the spoiler due to their incredible importance. Oh, and there is also a joke about centaur genitals, but it is also included solely for the context.

VoiceAttack is known in game lovers like Star Citizen and Elite: Dangerous, and is a fairly simple tool for associating keystrokes with voice commands. With a great desire, it can be used to control the PC, but it is fully revealed after putting on the head of VR-glasses

break for a joke about VR

')

... and launch any of the above games.

My first acquaintance with VoiceAttack took place while watching a wonderfully delivered video from Mr. Rimas, during which I involuntarily began to respect the Windows speech recognizer, who somehow understood such a terrible accent.

Since I didn't have a VR helmet (as, incidentally, no money for it), I decided to play around with the recently shared with me through Steam Elite: Dangerous, driving a standard ship with a voice. Running to VoiceAttack, I marveled at the developers appetites (10 bucks for the wrapper of the built-in speech recognizer), and immediately decided for myself that I would use the maximum demo version of the program. However, when I tried to download the downloaded profile, I found another gift from the developers - the demo version of the program fails to save more than 20 commands at the same time, which, of course, complicates communication with the ship somewhat - the number of controls in E: D is clearly more than twenty.

The quest for pill greed




Being a proud pirate who did not want to pay 500 rubles for a license, I climbed into the Internet for a crack. I was disappointed in the form of a heap of broken links, ransomware, and other dregs offered by the Network in the absence of the necessary data. The only piece of information was found on the exetools forum, but described the old version of the program. We had to do something, or spit and do nothing already.

Research work


So, for starters, just run VoiceAttack. After the certificate, as expected, you will be asked to register the key:



Referring to the online help, we learn that the key is sixteen-digit, and consists of both numbers and letters:



After trying to fill the fields with garbage, it became clear that there are checks on the validity of the key:





We enter everybody's favorite test@example.com, 16 digits, and admire checking the key on the server, and the error message:





Actually, the exploration on this can be completed. The principle of registration is quite simple - the program explicitly communicates with the remote server, transmits our registration data, and analyzes the answer. Such a method is corrected either by a program patch so that it does not climb into the network and immediately considers the key to be correct, or, in order to satisfy Monsieur’s interests, the registration server emulator is written. And this will do.

Analysis and Deobfuscation


For analyzing executables, I like to use exeinfope - cheap and cheerful (and there is a line of information for lamers!)

Open VoiceAttack.exe in exeinfope:



Great, we were lucky - .NET + SmartAssembly is akin to a jackpot - whether the program was written in C ++ (and God forbid just under x64), or obfuscated with something more interesting, you would have to poke around in a spaghetti crookedly disassembled output. And so we can easily do with the dnSpy decompiler and de4dot deobfuscator.

Copy the program files to a separate folder and deobfustsiruem:



We are ready for debug and code analysis.

Analysis of network activity registration


First, let's launch VoiceAttack, and enter our invalid registration data. Click OK, and quickly stop the program in the debugger. We see the following:



As we thought, during registration, the program knocks on the voiceattack.com server and sends registration data there. We take out the request string:

http://voiceattack.com/Validate.aspx?pv=JcArVyeUmUOZfJVj6utdKw==&em=iEm1cpNSBqMsA06LJExtLntuDo0yvQwPzKuIJhhbLt8=&vk=A1Zkz8zPx2VUdM1oi+mKHHtuDo0yvQwPzKuIJhhbLt8=&sg=OBaQB9KBl8iHF1miBzpp/Q==&nn=gXWP+H1uDbYW+crJfgFNs8gexDihTyvNmjpBzp/I0//f3IvaGDFmFz+ll1WxqPdu6iC0SAGY1eJBMRvl2GIr2A==&pr=0+jAkAfwc3I1ZMHk2zdDz3tuDo0yvQwPzKuIJhhbLt8= 

A new problem is immediately visible - obviously more parameters are being transmitted to the server than we set, and also in base64. We try to decode base64:



Not bad, the data is also encrypted. Need to understand further.

Encryption analysis and hacking


Find in dnSpy registration form. After a brief search, we find the appropriate form frmRegister and climb into the event code btnOk_Click:



It seems, that is necessary. Visible texts already familiar to us errors. Let's rewind the code to the moment of connection with the server we need:



The HTTP response is written to the response variable. Find where the encrypted response body is written:



We turn the code further, looking for the mention of the text8 variable and stumble upon its rewriting with the help of a certain function:



Go to the function and see what hit the mark:



Obviously, the function implements AES-decryption of the input string, and returns the decrypted result. Now all that remains is to catch the key contained in the variable this.byte_0. Set breakpoint and run the program. At the time of reaching breakpoint, we climb into the values ​​of variables:



Now we have all the turnout and passwords. You can begin work on the registration server emulation.

Registration server


To begin with, we will write an encryption and decryption tool based on the found key:

 using System; using System.Security.Cryptography; using System.Text; public class Program { public static void enc(string text) { string result; Rijndael rijndael = Rijndael.Create(); rijndael.Mode = CipherMode.ECB; byte[] bytes = Encoding.ASCII.GetBytes(text); byte[] key = {0x74,0x72,0x75,0x65,0x47,0x52,0x49,0x54}; //  result = Convert.ToBase64String(rijndael.CreateEncryptor(key, null).TransformFinalBlock(bytes, 0, bytes.Length)); Console.WriteLine(result); } public static void dec(string text) { string result; Rijndael rijndael = Rijndael.Create(); rijndael.Mode = CipherMode.ECB; byte[] key = {0x74,0x72,0x75,0x65,0x47,0x52,0x49,0x54}; byte[] bytes = Convert.FromBase64String(text); result = Encoding.ASCII.GetString(rijndael.CreateDecryptor(key, null).TransformFinalBlock(bytes, 0, bytes.Length)); Console.WriteLine(result); } public static void Main(string[] args) { if(args[0]=="enc") { enc(args[1]); } else if (args[0]=="dec") { dec(args[1]); } } } 

Let's try to decrypt the parameters transmitted to the server:



Fine. Now we can see what the program sends to the server after all. We will request the result yourself on the url already familiar to us and decrypt it:



It seems that the server sends the answer in the form of%. The result is validation% _% Information%. Turn again to the code and find out what answer means success:



Quickly run to bring www.voiceattack.com to hosts, encrypt the string "Validation Success_some useless info" by our program, host the page, register the program ...

And we receive in response a whole cookie in butter - when opening the program again offers to register. This behavior means only one thing - a license check has been implemented at startup.

We finalize the server


Again we climb into the decompiled code. We find the method of loading the main form frmMain_Load and after a brief search we see the code for invoking the form to register:



Pay attention to the condition of the transition to the form load branch - some variable is checked this.bool_89. Let's wind off the code a little higher and see how its value is formed:



There is a check of compliance of one line with another. On the right is a ValidationKey, on the left is the application of the method on a list of strings. What method do you think is alternately applied to all these lines?

We set breakpoint on the comparison string to find out the values ​​of the string_ and text strings. We get:



What values ​​are substituted into the registrationEmail and registrationKey strings, I think that's understandable.

We are finalizing the server for parsing a GET request and generating a suitable response:

 from SimpleHTTPServer import BaseHTTPServer import SocketServer import subprocess from urlparse import urlparse from urllib import unquote import ctypes, sys, os PORT = 80 def aes(action, text): return subprocess.Popen(["VoiceAttackCipher.exe", action, text], stdout=subprocess.PIPE).communicate()[0][:-2] def patch_hosts(): with open("C:\WINDOWS\System32\drivers\etc\hosts", "a") as hosts_file: hosts_file.write("\n127.0.0.1 www.voiceattack.com") def unpatch_hosts(): with open("C:\WINDOWS\System32\drivers\etc\hosts", "r") as hosts_file: hosts_lines = [] for line in hosts_file: if line != "127.0.0.1 www.voiceattack.com": hosts_lines.append(line) hosts_lines[-1] = hosts_lines[-1].strip("\n") with open("C:\WINDOWS\System32\drivers\etc\hosts", "w") as hosts_file: for line in hosts_lines: hosts_file.write(line) class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler): def do_HEAD(s): s.send_response(200) s.send_header("Content-type", "text/html") s.end_headers() def do_GET(s): """Respond to a GET request.""" query = urlparse(s.path).query query_components = dict(qc.split("=") for qc in query.split("&")) print "Got registration request! Processing..." key = aes("dec", unquote(query_components["vk"])) mail = aes("dec", unquote(query_components["em"])) magic_1 = aes("dec", unquote(query_components["pv"])) magic_2 = aes("dec", unquote(query_components["sg"])) print "Key:", key print "E-Mail:", mail print "Magic number #1:", magic_1 print "Magic number #2:", magic_2 print "Creating validation key..." validation_key = aes("enc", "{}{}{}{}".format(magic_1, mail, key, magic_2)) print "Validation key:", validation_key print "Creating response..." response = aes("enc", "Validation Success_{}".format(validation_key)) print "Response:", response s.send_response(200) s.send_header("Content-type", "text/html") s.end_headers() #   "Validation Success_{}".format(enc(dec(pv)+dec(em)+dec(vk)+dec(sg))) s.wfile.write(response) print "Done! Your program should be registered now!" if __name__ == "__main__": print "Starting VoiceAttack Activation Server..." print "Checking privileges..." is_admin = ctypes.windll.shell32.IsUserAnAdmin() != 0 if not is_admin: print "Error: please run this program with admin rights." os.system("pause") sys.exit() print "All good." print "Patching hosts..." patch_hosts() print "VoiceAttack Server Started!" print "You can try to register VoiceAttack." print "Press [Ctrl+C] after successful registration." try: httpd = SocketServer.TCPServer(("", PORT), MyHandler, bind_and_activate=False) httpd.allow_reuse_address = True httpd.server_bind() httpd.server_activate() httpd.serve_forever() except KeyboardInterrupt: httpd.server_close() except Exception as e: print e finally: print "Voice Attack Server is shutting down..." #print "Un-patching hosts..." #unpatch_hosts() #    ,        print "Bye-bye." os.system("pause") 

Instead of conclusion


All of the above was done solely out of sports interest, and even more from happiness that with the next attempt to unzip the program, I will not have to spit on Ida's listings. I wanted to give birth to my first post for a long time, and here I was also invited by a UFO - so I urge you to write in the comment if something is wrong or somehow not very.

Source: https://habr.com/ru/post/347800/


All Articles