Monday, May 4, 2015

Reverseing reverse engineered tools: reverse on the pyc of Easy-Card tool

A hacker called  Zhi-Wei Cai did some reverse engineering on Taipei city passport Easy Card credit querying system.


https://github.com/x43x61x69/Easy-Card


But he only released the .pyc file.

I tool a glance on the binary file and smiled, he didn't apply any code obfuscation on that, that is, it would be pretty easy for those who want to read the source code.

With the help of this tool: https://github.com/wibiti/uncompyle2

uncompyle2 easycard.pyc > easycard.py

You can decompile that yourself.




Basically the API takes 4 parameters: verify, cardID, begin, end.


verify = md5((seed * const) + salt)
where
    seed = date.month + date.day + date.hour
    salt = 'L0CalKing'
    const = 8544

cardID = base64( des3( data, key, iv, mode=DEC3.MODE_CBC) )
where
    data = 'your card ID', like '1234567889'
    key = 'EasyCardToKingay23456789
    iv = '01234567'
   
begin / end = time period


I'm really curious about how did he get these constants , but I don't want to dig up the original app. :)


Lesson: don't release .pyc file without code obfuscation if you really don't want any people try to dig up your code.


Code listing (partially omitted):

#!/usr/bin/env python2
# -*- encoding: utf8 -*-

# 2015.05.04 17:41:47 CST
import sys
import datetime
import hashlib
import urllib
import urllib2
import json
from Crypto.Cipher import DES3
import pytz
version = '0.3'
copyright = 'Copyright (C) 2015 Zhi-Wei Cai.'
key = 'EasyCardToKingay23456789'
iv = '01234567'
salt = 'L0CalKing'
const = 8544

def getID(data, isEncrypt, key, iv, encode):
    size = len(data)
    # '\x06' is the padding of DES3
    if size % 16 != 0:
        data += '\x06' * (16 - size % 16)
    des3 = DES3.new(key, DES3.MODE_CBC, iv)
    if isEncrypt:
        result = des3.encrypt(data).encode(encode).rstrip()
    else:
        result = des3.decrypt(data.decode(encode))
    return result



def getVerify(const, seed, salt):
    hash = hashlib.md5()
    hash.update(str(seed * const) + salt)
    return hash.hexdigest().upper()



def proc(data):
    e = getID(data, 1, key, iv, 'base64')
    cardID = urllib.quote_plus(e)
    date = datetime.datetime.now(pytz.timezone('Asia/Taipei'))
    seed = date.month + date.day + date.hour
    begin = '{:%Y-%m-%d}'.format(date - datetime.timedelta(days=30))
    end = '{:%Y-%m-%d}'.format(date)
    verify = getVerify(const, seed, salt)
    url = '<Easy Card API URL>'.format(verify, cardID, begin, end)
    req = urllib2.Request(url)
    response = urllib2.urlopen(req)
    content = response.read()
    dict = json.loads(content)

   # the rest part of the code is omitted


if __name__ == '__main__':
    print '\n悠遊卡餘額明細查詢 v{}'.format(version)
    print '{}\n'.format(copyright)
    if len(sys.argv) > 1:
        try:
            print '\n{:=^90}\n'.format('[ 查詢開始 ]')
            proc(str(sys.argv[1]))
        except ValueError as err:
            pass
    else:
        while 1:
            try:
                data = raw_input('請輸入卡片號碼:').replace(' ', '')
                if len(data):
                    print '\n{:=^90}\n'.format('[ 查詢開始 ]')
                    proc(data)
                else:
                    break
            except ValueError as err:
                pass


#+++ okay decompyling easycard.pyc
# decompiled 1 files: 1 okay, 0 failed, 0 verify failed
# 2015.05.04 17:41:47 CST

No comments:

Post a Comment