2013년 8월 22일 목요일

[Write-Up] May the HeXA with you

HeXA 1st CTF Rev 300

May the HeXA with you

Download

link : https://www.dropbox.com/s/rypuwxcpj8taj6r/HeXATalk.apk

Category

  • network
  • coding

Description (http://goo.gl/HUsdRA)

A long time ago in a galaxy
far, far away...

Episode VII
May the hexa with you

It is a dark time for the Rebellion. Although the Death Star has been destroyed, Imperial troops have driven the Rebel forces from their hidden base and pursued them across the galaxy.

To overcome this serious crisis, Rebellion request for a hacker group, HeXA, to develope an encrypted  network. Therefore, HeXA start work on the new project named HeXATalk.

However, just before the completion of HeXATalk, Imperial's assassin killed all the members of HeXA except you. Rebellion, who must get a secret message from Luke Skywalker, need you to complete the unfinished HeXATalk and find out what the secret message is.

Write-Up


  1. unzip apk
  2. extract jar with dex2jar : $dex2jar HeXATalk.apk
  3. jar analysis



source code : hexa.per.sh.hexatalk.MainActivity$p

package hexa.perl.sh.hexatalk;

import android.annotation.SuppressLint;
import android.os.AsyncTask;
import android.util.Log;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Random;

class MainActivity$p extends AsyncTask
{
  private MainActivity$p(MainActivity paramMainActivity)
  {
  }

  @SuppressLint({"NewApi"})
  protected Void doInBackground(Void[] paramArrayOfVoid)
  {
    try
    {
      if (InetAddress.getByName(this.this$0.HOST).isReachable(1000))
      {
        this.this$0.suc = true;
        ByteBuffer localByteBuffer = ByteBuffer.allocate(30);
        localByteBuffer.order(ByteOrder.LITTLE_ENDIAN);

        // localByteBuffer1 = 4바이트 int (인덱스)
        localByteBuffer.putInt(0, this.this$0.idx);
        char[] arrayOfChar1;

        int i; // 인덱스
        if (this.this$0.idx == 1)
        {
          arrayOfChar1 = new char[] { 108, 111, 103, 105, 110 }; // char[] 'login'
          i = 0;
          if (i < arrayOfChar1.length); // 첫번째 단계
        }
        while (true)
        {
          // 소켓 연결
          MainActivity.access$0(this.this$0, new Socket(this.this$0.HOST, this.this$0.PORT));
          MainActivity.access$2(this.this$0, MainActivity.access$1(this.this$0).getInputStream());
          MainActivity.access$3(this.this$0, MainActivity.access$1(this.this$0).getOutputStream());
          MainActivity.access$4(this.this$0).write(localByteBuffer.array());
          MainActivity.access$4(this.this$0).flush();
          this.this$0.wsize = MainActivity.access$5(this.this$0).read(this.this$0.w);
          MainActivity localMainActivity = this.this$0;
          localMainActivity.idx = (1 + localMainActivity.idx);
          MainActivity.access$6(this.this$0);
          return null;
          int j = i + 4;

          // localByteBuffer = 4바이트 int (인덱스) + char[] 'login' (커맨드)
          localByteBuffer.put(j, (byte)arrayOfChar1[i]);

          i++; // 인덱스 증가
          break;
          if (this.this$0.idx == 2) // 두번째 단계
          {
            String str = new String(this.this$0.w, 0, this.this$0.wsize);

            // 서버로부터 받은 메세지 => port : xxxxxxx
            // this.this$0.PORT = xxxxxxx (새로 할당받은 포트번호)
            this.this$0.PORT = Integer.parseInt(str.substring(3 + str.indexOf(" : ")));

            char[] arrayOfChar2 = { 98, 117, 121 }; // char[] 'buy'
            for (int k = 0; ; k++)
            {
              // 루프 마지막에 한번
              if (k >= arrayOfChar2.length)
              {
                this.this$0.aes_key = new byte[16];
                new Random().nextBytes(this.this$0.aes_key);

                // aes_key에 랜덤 바이트 할당
                for (int m = 0; m < this.this$0.aes_key.length; m++)
                  localByteBuffer.put(m + 14, this.this$0.aes_key[m]);

                // localByteBuffer = 4바이트 int (인덱스) + char[10] 'buy' (커맨드) + aes_key (랜덤 바이트 AES 키)
                break;
              }

              // localByteBuffer = 4바이트 int (인덱스) + char[4] 'buy' (커맨드)
              localByteBuffer.put(k + 4, (byte)arrayOfChar2[k]); 
            }
          }
          if (this.this$0.idx == 3) // 세번째 단계
          {
            // 완성되지 않은 코드 = 서버로부터 데이터를 받는 부분
            // 4바이트 int (암호화된 데이터 크기) + AES/CBC/PKCS7Padding 암호화된 데이터
            Log.d("HeXA", "need to read  4byte ([x] = size of encrypted data) + [x] byte ( AES/CBC/PKCS7Padding encrypted data )");
            byte[] arrayOfByte = new byte[16];
            arrayOfByte[0] = 104;
            arrayOfByte[1] = 101;
            arrayOfByte[2] = 120;
            arrayOfByte[3] = 97;
            arrayOfByte[4] = 102;
            arrayOfByte[5] = 111;
            arrayOfByte[6] = 114;
            arrayOfByte[7] = 101;
            arrayOfByte[8] = 118;
            arrayOfByte[9] = 101;
            arrayOfByte[10] = 114;
          }
        }
      }
    }
    catch (Exception localException)
    {
      while (true)
      {
        this.this$0.suc = false;
        continue;
        this.this$0.suc = false;
      }
    }
  }
}

4. coding


import socket
import sys
import struct
import time
from Crypto.Cipher import AES
from pkcs7 import PKCS7Encoder

encoder = PKCS7Encoder()

def enc_aes(data):
    iv = struct.pack('<16s','hexaforever')
    aes = AES.new(key=aes_key, mode=AES.MODE_CBC, IV=iv)
    pad_text = encoder.encode(data)
    cipher = aes.encrypt(pad_text)

    return cipher

def dec_aes(data):
    iv = struct.pack('<16s','hexaforever')
    aes = AES.new(key=aes_key, mode=AES.MODE_CBC, IV=iv)
    pad_text = aes.decrypt(data)
    plain_data = encoder.decode(pad_text)

    return pad_text

HOST = "10.xx.xx.xx"
PORT = xxxx

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, PORT))

data = " ".join(sys.argv[1:])

idx = 1
data = struct.pack('<I', idx)
data += struct.pack('<10s','login')

sock.sendall(data + "\n")
received = sock.recv(1024)
sock.close()

print "Sent:     {}".format(data)
print "Received: {}".format(received)

port = received.split(" : ")[1]
print "port : " + str(port)

#print "sleep for 3 sec"
#time.sleep(10)

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, int(port)))

aes_key='\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
idx = 2
data = struct.pack('<I', idx)
data += struct.pack('<10s','buy')
data += aes_key

sock.sendall(data + "\n")
data = sock.recv(1024)
print data
sock.close()

size = struct.unpack('<I',data[:4])[0]
print "SIZE : " +str(size)
print dec_aes(data[4:4+size])

print "Received: {}".format(received)

Missing code


ByteBuffer bB = ByteBuffer.allocate(4);
bB.order(ByteOrder.LITTLE_ENDIAN);
bB.put(Arrays.copyOfRange(w, 0, 4));

int size = ByteBuffer.wrap(bB.array()).order(java.nio.ByteOrder.LITTLE_ENDIAN).getInt();

byte[] iv = new byte[] { 0x68, 0x65, 0x78, 0x61, 0x66, 0x6f, 0x72, 0x65, 0x76, 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00 };

javax.crypto.spec.SecretKeySpec keyspec = new javax.crypto.spec.SecretKeySpec(aes_key, "AES");
javax.crypto.spec.IvParameterSpec ivspec = new javax.crypto.spec.IvParameterSpec(iv);

javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("AES/CBC/PKCS7Padding");
cipher.init(javax.crypto.Cipher.DECRYPT_MODE, keyspec, ivspec);
byte[] decrypted = cipher.doFinal(Arrays.copyOfRange(w, 4, 4 + size));

String key = new String(decrypted);

댓글 없음:

댓글 쓰기