늘모자란, 개발

늘모자란, 개발


Assignment #3
주어진 평문, 암호문을 이용하여 사용된 두개의 키를 알아내는 과제

1. 암호문은 DES(ECB), AES-128(CBC)를 이용하여 암호화됨
2. 계산시간의 축약을 위해, password list가 주어진다. 리스트에는 md5 hash 값과, word가 한라인에 공백으로 구분되어 기재됨
2-1. 리스트는 약 18만 5천여개의 hash로 이루어져있다.
3. DES CBC이므로 8바이트 key를 사용해 암호화를 해야하므로, 주어진 hash에서 가장 앞글자 8자리를 사용할것
4. AES-128은 주어진 hash를 모두 사용해 암호화할것




외국사람들이 내 블로그 볼일이 있겠나 싶어 그냥 한글로 정리한다.

처음에 이 과제를 받고 어떻게 해야하나 생각을 해봤다.
평문과 암호화된 텍스트, 그리고 심지어 암호화 방법도 주어졌으니
암호화를 두번하고, 나오는 cipher text를 주어진 암호문과 비교 하는방법을 생각했다.

당연히, NP-hard Problem 이라고 생각했다. 그 외의 방법이 있나.. 싶었는데 내가 멍청이란걸 깨닫기전엔...
그래도 모르니 일단 코드 공유를 한다.
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os,sys,timeit,string,base64
from Crypto.Cipher import AES,DES

__author__ = "Song Myeong-Uk"
__date__ = "2015.05.27"

#global value
md5_keys = {}
target_plaintext = ""
encrypted_plaintext = ""

start_time = timeit.default_timer() #when program start

#@func: download_data
#@desc: If there is no md5 hash passwords, download it
def download_data():
  print "[-] passwords.txt not found! downloading now..."
  url = "http://www.ece.ubc.ca/~hyoung/passwords.txt"
  urllib.urlretrieve(url, 'passwords.txt')
  print "[-] passwords.txt download complete"

#@func: save_input_data
#@desc: Insert input data to global variable
def save_input_data():
  global target_plaintext
  global encrypted_plaintext

  i = 0
  with open('PlaintextCiphertext.txt', 'r') as f:
    i = 0
    for line in f:
      line = line.replace("\r\n","").replace("\n","")
      if i is 0:
        target_plaintext = line
      elif i is 1:
        encrypted_plaintext = line
      i = i + 1
  print "[-] Done!"

#@func: download_data
#@desc: To faster calculate. insert file data in memory instead IO system
def md5_to_memory():
  global md5_keys
  with open('passwords.txt', 'r') as f:
    for line in f:
      key_hash = line.split(" ")[0]
      password = line.split(" ")[1].replace("\r\n","").replace("\n","")
      md5_keys[key_hash] = password
  print "[-] Done!"

#@func: DES_encrypt
#@param hash: first selected key of text file
#@desc: Simple DES(ECB) encryptor. return encrypted text
def DES_encrypt(hash):
  global target_plaintext

  des = DES.new(hash[:8], DES.MODE_ECB)
  text = pad(target_plaintext,8)
  cipher_text = des.encrypt(text)

  return cipher_text

#@func: AES_encrypt
#@param DESed: DES encrypted text
#@param hash: Second selected key of text file
#@desc: Simple AES(CBC) Encryptor. return encoded base64 text 
def AES_encrypt(DESed,hash):
  global target_plaintext
  
  #128 bits = 16bytes

  mode = AES.MODE_CBC
  encryptor = AES.new(hash, mode, IV='\x00'*16)
  text = pad(DESed,16)

  ciphertext = encryptor.encrypt(text)
  return ciphertext.encode('base64')

def s2b(x):
  return ''.join(c.encode('hex') for c in x)

#@func: decrypt
#@param enc: encrpyted text
#@param k1: First selected key of text file
#@param k2: First selected key of text file
#@desc: just test case
def decrypt(enc,k1,k2):
  # enc = "W7n515icc+1dW5+82CYgPOGyFqgaFLA0FTCgB/jw1DZBeKUUF2h4z7yRWb03ZIeE"
  # k1 = "0001245350b5eb0a1548fc6d27d3b4d1"
  # k2 = "00009965"
  text = enc
  dec = AES.new(k1, AES.MODE_CBC, IV='\x00'*16)
  des = DES.new(k2[:8], DES.MODE_ECB)
  return unpad(des.decrypt(pad(unpad(dec.decrypt(text.decode('base64'))),8)))

#@func: pad
#@param x: text
#@param b: limit
#@desc: add null character to text untill b
def pad(x,b):
  pad1 = lambda s: s + (b - len(s) % b) * chr(b - len(s) % b) 
  return pad1(x)

def unpad(x):
  try:
    unpad1 = lambda s : s[0:-ord(s[-1])]
    return unpad1(x)
  except Exception,err:
    return x
  

#@func: progress
#@desc: execute calc when it ready
def progress():
  global md5_keys
  try:
    for hash in md5_keys:
     for hash2 in md5_keys:
       DESed_text = DES_encrypt(hash)
       if encrypted_plaintext is AES_encrypt(DESed_text,hash2):
         print "[-] find keys ... !!"
         print md5_keys[hash]
         print md5_keys[hash2]
         raise BreakAllTheLoops()
       else:
        #debug to print. you can pass it
        #pass
        print "[-] Trying key : {} - {},{}".format(AES_encrypt(DESed_text,hash2),md5_keys[hash],md5_keys[hash2]).replace("\r\n","").replace("\n","")
    print "[-] Can not find keys ... "
  except BreakAllTheLoops:
    pass

  # for hash in md5_keys:
  #   for hash2 in md5_keys:
  #     if target_plaintext is decrypt(encrypted_plaintext,hash2,hash):
  #        print hash
  #        print hash2

# main execution
if __name__ == "__main__":
  print "[+] starting {}...".format(os.path.basename(__file__))
  # check if data file exists

  #check input file exists
  cipher_exists = os.path.isfile('PlaintextCiphertext.txt')
  print "[+] checking input file: PlaintextCiphertext.txt"
  
  if not cipher_exists:
    print "[+] PlaintextCiphertext.txt not found!"
    sys.exit()
  else:
    print "[-] Done!"

  print "[+] checking password data: passwords.txt"
  txt_exists = os.path.isfile('passwords.txt')
  if not txt_exists:
    download_data()
  else:
    print "[-] Done!"

  print "[+] Process init ... [1/2]"
  save_input_data()

  print "[+] Process init ... [2/2]"
  md5_to_memory()

  print "[+] Start processing ... "
  print "[-] Target plaintext : {}".format(target_plaintext)
  print "[-] Target Encrpyted key : {}".format(encrypted_plaintext)
  progress()

  stop_time = timeit.default_timer() #when program end

  print "Done!!"
  print "Program Run Time : " + str(stop_time - start_time)


코드는 단순하다. 먼저 DES를 하고 AES를 해서 최종
총 키가 18만개니까, 최악의 경우를 고려하면 O(n^2) 가 되는 방법이다.
1초에 30개씩 처리한다고 해도 며칠은 돌려야된다.. 잘될리가 없었다. 속도문제도 그렇고 퍼포먼스가 나오지 않았다.
이대로는 안되겠다 생각했다. 문제를 다시 읽어서 교수님이 원하는게 뭔가 생각을 했다.

왜 하필이면 두번만했을까, 왜 더블 AES도 아니고 다른 암호화를 썼을까
처음에 접근했어야 하는 문제를 개삽질 한번하고 다시 생각해보게 되었다.

그 과정에 Double DES를 생각하게 되었다.
Double DES 에 대한건 영상을 하나 첨부한다.

요점만 말하자면, 첫번째 암호화 한 결과와 암호화된 값을 복호화함으로서 생기는 중간값이 매치가 되야 정상 프로세스가 진행되게 된다. 즉, 중간값을 알아내기 위한 MITM(Meet in the middle) 공격이 가능해진다. (기존의 MITM이랑은 좀 다르다)

따라서, AES는 decrpyt 하고 DES는 encrpyt 하면 반드시 매칭되는 중간 값이 나오게 된다.
속도도 O(nx2)가 맥시멈으로서 제곱과는 비교가 되지 않는건 물론이고...
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os, sys, time, base64, string, urllib
from Crypto.Cipher import AES, DES
from Crypto.Hash import MD5
 
# author information
__author__ = "Song Myeong-Uk"
__email__ = "mwsong@imtl.skku.ac.kr"
__date__ = "2015.05.27"
 
# constants
DATA_FILE_URL = "http://www.ece.ubc.ca/~hyoung/passwords.txt"
DATA_FILE_NAME = "passwords.txt"
INPUT_FILE_NAME = "PlaintextCiphertext.txt"
OUTPUT_FILE_NAME = "keys.txt"
 
# global variables
md5_keys = {}
target_plaintext = ""
encrypted_plaintext = ""
DES_keys = {}
AES_keys = {}
 
# pad value with given block size: 16
def pad(x):
  BLOCK_SIZE = 16
  if len(x) == BLOCK_SIZE: return x
  c = (len(x) // BLOCK_SIZE) + 1
  c *= BLOCK_SIZE
  return "{:\x00<{l}}".format(x, l=c)
 
# returns value of md5 for given value
def hashMD5 (v):
  m = MD5.new(v)
  return m.digest()
 
# download base md5 hash password list from url
def download_data():
  global DATA_FILE_URL, DATA_FILE_NAME
  print "[-] {} not found! downloading now...".format(DATA_FILE_NAME)
  urllib.urlretrieve(DATA_FILE_URL, DATA_FILE_NAME)
  print "[-] {} download complete".format(DATA_FILE_NAME)
 
# calculate run time
def print_time(t):
  diff = time.time() - t
  print "[*] total run time: {:.4f} seconds".format(diff)
 
# load data from input file
def save_input_data():
  global target_plaintext, encrypted_plaintext
  global INPUT_FILE_NAME
  with open(INPUT_FILE_NAME, 'r') as f:
    data = [ l.strip() for l in f.readlines() ]
    target_plaintext = data[0]
    encrypted_plaintext = data[1]
 
# load md5 data into dictionary
def md5_to_memory():
  global md5_keys
  global DATA_FILE_NAME
  with open(DATA_FILE_NAME, 'r') as f:
    data = [ l.strip() for l in f.readlines() ]
    for line in data:
      line = line.split(' ')
      md5_keys[line[0]] = line[1]
 
# encrypts plaintext with DES using different keys
def plaintext_to_des():
  global md5_keys, DES_keys
  for k in md5_keys:
    KEY = DES_encrypt(hashMD5(md5_keys[k])[:8]).encode('hex')[:64]
    DES_keys[KEY] = md5_keys[k]
 
# decrypts ciphertext with AES128 using different keys
def enc_to_decrypt():
   global encrypted_plaintext, md5_keys, AES_keys, DES_keys
   for k in md5_keys:
    KEY = AES_decrypt(encrypted_plaintext, hashMD5(md5_keys[k])).encode('hex')[:64]
    AES_keys[KEY] = md5_keys[k]
    try:
      if DES_keys[KEY]:
        print "[!] solution key found"
        print "[-] key1: {}".format(DES_keys[KEY])
        print "[-] key2: {}".format(AES_keys[KEY])
        write_key(DES_keys[KEY], AES_keys[KEY])
      else:
        print "[!] solution key not found"
    except Exception, err:
      pass
 
# writes found key to file
def write_key(k1, k2):
  global OUTPUT_FILE_NAME
  print "[+] writing key file...",
  with open(OUTPUT_FILE_NAME, 'wb') as f:
    f.write(k1 + '\n' + k2)
  print "done"
 
# DES-ECB encrypt with given key
def DES_encrypt(key):
  global target_plaintext
  return DES.new(key, DES.MODE_ECB).encrypt(pad(target_plaintext))
 
# AES-CBC decrypt text with given key
def AES_decrypt(text, key):
  return AES.new(key, AES.MODE_CBC, IV='\x00'*16).decrypt(base64.b64decode(text))
 
# main execution block
def main():
  print "[+] starting {}...".format(os.path.basename(__file__))
  # time function execution
  start_time = time.time()
  # check if base data file exists
  txt_exists = os.path.isfile(DATA_FILE_NAME)
  print "[+] checking password data: {}".format(DATA_FILE_NAME)
  if not txt_exists:
    download_data()
  # check if input file exists
  cipher_exists = os.path.isfile(INPUT_FILE_NAME)
  print "[+] checking input file: {}".format(INPUT_FILE_NAME)
  if not cipher_exists:
    print "[!] {} not found! terminating...".format(INPUT_FILE_NAME)
    print_time(start_time)
    sys.exit()
  # load input data
  print "[+] loading input file data..."
  save_input_data()
  # load md5 to memory
  print "[+] preparing md5 dictionary..."
  md5_to_memory()
  # DES encrypt
  print "[+] processing with DES... [1/2]"
  plaintext_to_des()
  # AES decrypt
  print "[+] processing with AES... [2/2]"
  enc_to_decrypt()
  # time execution
  print_time(start_time)
 
if __name__ == "__main__":
  main()


7초만에 프로세스는 끝난게 된다.
여러 다른 어프로치를 했봤으나, 실질적인 소득은 위 두가지 코드가 전부이고, 따로 레퍼런스랄것도 이번에 없다.
Meet-in-the-Middle 공격을 잘 이해만 한다면 쉽게 구현할 수 있다.

최근에는 Single DES 및 Double DES는 아예 쓰이지도 않고, 중간자 공격에 대비해 최소 Triple DES를 사용하고,
이마저도 AES가 표준이 됨으로서 DES가 많이 사용되고 있지는 않지만, 암호화를 하려는 사람들은 이런 Known Ciphertext Attack 을 항상 염두에 둬야할것이다.






Reference
https://www.youtube.com/watch?v=vROZGQ9XLe8
2015/05/28 23:42 2015/05/28 23:42
Assignment #1

Requirements
1. At least 8 characters long
2. Contains both upper and lower-case letters
3. Contains one or more numerical digits and special characters.
4. Must be written in C/C++/Java/Python
5. Time limit for key/100 seconds
6. No salt keys. -> http://www.xorbin.com/tools/sha256-hash-calculator
7. All Program is smaller than 30MB



블로그 글을 너무 대충 작성해서 다시 쓰기로 했다. 구글에 검색하니 나오는게 신기하기도 했고..

먼저 이 과제의 목적은, 일반적으로 알려진 비밀번호를 사용하면 안된다는 것이다.
대부분의 비밀번호로 사용되는 hash 값들은 salt를 이용하여 이중 비밀번호를 진행한다.
따라서 one way 암호화를 진행하는데, 이 과정을 거치지 않을 경우에 비밀번호가 저장된 데이터베이스가 털렸을시 비밀번호 guessing이 가능하다는것을 알려주고자 하는 과정이다

요즘은 비밀번호 자릿수를 무조건 8자리이상에, 특문 하나를 넣어야 된다지만,
예전엔 그렇지 않았다는것을 누구나 안다. 6자리이상에 숫자 안넣어도, 아니, 숫자만 넣어도 비밀번호를 넣을 수 있었다.
이런 취약점들덕에 비밀번호 제도가 바뀌게 되었는데... 사설이 너무 길었고

어쨌든 이 과제에서는 SHA256으로 암호화된 비밀번호를 역으로 알아내야 하는것인데, 공격법은 세가지 정도로 나뉜다.

1. 역상 공격(preimage Attack)
  Secure한 SHA256상대로 할 수 없다. 할 수 있을진 모르겠는데 일단 내 실력으론 어림도 없는건 확실했다.

2. 무차별 대입 공격(Brute force Attack)
  SHA256에 할당된 8자리의 레인보우 테이블만 4테라바이트라고 한다....

3. 사전 공격(Dictionary Attack)
  결국 세가지 방법중에 미리 정의한 잘 알려진 단어와 유출된 사용자들이 자주 쓰던 비밀번호를 사용해서 사전을 만들고, 그를 SHA256 hash로 다시 정의하여 찾는 방법이 합당해보였다


우선 Password Dictionary라고 검색하면 어렵지 않게 정의된 사전들을 찾을 수 있다.
처음에 접근해보길, 모든 단어를 커버할 수 없으니 8자리만을 선택하자. 그런데 대소문자가 반드시 필요되시되고 특문 하나 숫자 하나도 들어가니까, 임의의 글자를 만들어 넣자라고 생각했으나, 프로그램 크기가 30메가 제한이라는 소식을 접하게 되었다.

처음엔 6자리 단어에 임의로 특문도 넣고 그랬는데 그랬더니 사전의 크기가 너무 커지게 되어서 비효율적이게 되었다.
그리고 만들어놓고 보니 Pass1[#2 뭐 이런꼴인데 사실 이렇게 비번 쓰는 사람도 없을 것 같았다..
그래서 딕셔너리를 다음과 같은 조건으로 거르고 해싱을 해보았다.

조건은,
1. 8~20자리의 글자
2. 숫자가 포함되어 있을것
3. 대문자가 포함되어 있을 것
i = 1
with open("file.txt") as f:
 for line in f:
     raw_pw = line.split(":")[0].replace('\r\n','').replace('\n','')
     if len(raw_pw) < 20 and len(raw_pw) >= 8:
          if re.search("[\!\-\~]",raw_pw) > -1:
               if re.search("[0-9]",raw_pw) > -1: 
                  if re.search("[A-Z]",raw_pw) > -1: 
                     hash_text = hashlib.sha256(raw_pw).hexdigest()
                     fp = open("aa.out","a")
                     fp.write(str(i)+":"+raw_pw+":"+hash_text+"\r\n")
                     fp.close()
                     i += 1

더 깔끔하게 코딩할 수 있을것 같은데 일단 이런식으로 해봤다..
이렇게 하면 백몇십메가나 되던 사전의 크기가 엄청나게 압축된다.
실제로 백만줄이 넘어가던 사전이 1메가도 안된다 지금은..
크기는 30메가 제한인데 제출과 현재 블로그 글을 쓰면서도 불안하나 그냥 하늘에 맡기기로 했다..
그 결과는 이곳(Github)에 있다.

나는 사실 여기까지 만들면서 사전만 잘 구성하면 될거라고 생각 했다. 사실 그게 핵심이고..
하지만 문제가 있었는데, 제출당일날 속도 제한이 있다는것을 깨달았다.
테스트로 돌려본 코드는 I/O를 이용해서 1개 키를 매칭해서 찾고 매칭해서 찾고 하는것이었다.
이건 망했으니 github엔 올리지 않고 여기에서 다룬다..
 import timeit
start = timeit.default_timer()

i = 1
with open("hashedPasswords.txt") as target:
      for line in target:
          user = line.split(":")[1].replace("\r\n","")
          target_word = line.split(":")[2].replace("\r\n","")
          with open("out_a.out") as dic:
              for dic_word in dic:
                  if target_word == dic_word.split("|")[1].replace("\r\n",""):
                      findit = dic_word.replace("\r\n","")
                      fp = open("Passwords.txt","a")
                      fp.write(str(i)+":"+user+":"+findit.split("|")[0]+"\r\n")
                      fp.close()
                      i+=1
                      break
                  
stop = timeit.default_timer()
print "Program Run Time : " + str(stop - start)


굳이 이름을 붙여보자면, Find word in large text file using python... zzz...
기가 막히지만, 말그대로다. 파일 포인터를 두개 열고 일대일로 나올때까지 매칭된다.
아마 사전의 크기가 크고 시도되는 키가 많다면 밤새도록 돌려야될지도 모른다.
내가 쓰려고 짰던건 아니고... 뭐 그렇다.

나는 이거랑 다르게 쓰레드를 이용해보기로 했다.
Python은 싱글코어만 사용하기때문에 쓰레드가 좋지 않아 프로세스를 늘리는 방식으로 사용해야 된다고 본적이 있다.
(참고: 파이썬으로 클라우드 하고 싶어요)

그래서 애초에 프로세스를 한개 더 늘려서 만들어보기로 했다.
사실 syncronized variable을 어떻게 만들어야될지 감도 안와서 추잡하게 다 쓰고 다시 읽어서 넘버링하고, 임시파일을 지우는 꽁수를 적어놨는데, 나는 코드의 신에게 분명히 맞아죽을것이다...
어쨌든 시도 자체는 훌륭했다고 생각한다. 프로세스를 물려서 뭔가 처릴 해보려고 했으니....... 남는건 있었겠지.
이렇게 돌린 코드는... 600초가 소모됐다 -_-
#!/usr/bin/env python

# Author     : Myeong-Uk (mwsong@imtl.skku.ac.kr)
# Date       : 2015. 03. 30
# Desc       : Python program to dictionary attack SHA256
# Dependency : out_c.out (Password dictionary), hashedPasswords.txt(input)
# Command    : <python find.py>
# OUTPUT     : Passwords.txt

import timeit,os
from multiprocessing import Process,Lock
  
  
start_time = timeit.default_timer() #when program start

def do_work(type):
    hash_count = 0
    with open('aa.out') as target:
         for line in target:
            findit = ""
            if type == "1" and hash_count%2 == 0:
                findit = search_word(line)
            elif type== "2" and hash_count%2 > 0:
                findit = search_word(line)
            hash_count +=1

            if findit != "" and findit != None:
                 user = findit.split("|")[0]
                 crackedPassword = findit.split("|")[1]

                 fp = open("Passwords_tmp.txt","a")
                 fp.write(user+":"+crackedPassword+"\r\n")
                 fp.close()

  
def search_word(word):
    user = str(word.split(":")[1].replace("\r\n",""))
    target_hash = word.split(":")[2].replace("\r\n","")

    with open("out_c.out") as dic:
        for dic_line in dic:

            dictionary_hash = dic_line.split("|")[1].replace("\r\n","")
            dictionary_word = dic_line.split("|")[0].replace("\r\n","")

            if target_hash == dictionary_hash:
                 return user + "|" + dictionary_word
                 break
 
def numbering():
    i = 0
    with open("Passwords_tmp.txt") as dic:
        for find_word in dic:    
             fp = open("Passwords.txt","a")
             fp.write(str(i)+":"+find_word)
             fp.close()
             i += 1
    print str(i) + " Password(s) found."

 
     
if __name__ == '__main__':

    pr1 = Process(target=do_work,args=(str(1)))
    pr2 = Process(target=do_work,args=(str(2)))
      
    pr1.start()
    pr2.start()
      
    pr1.join()
    pr2.join()


    stop_time = timeit.default_timer() #when program end

    print "Dictionary matching done"

    print "Numbering.."
    numbering()
    os.remove("Passwords_tmp.txt")
    print "Done!!"

    print "Program Run Time : " + str(stop_time - start_time)


사실 이걸 다 짜고 낼려는데 그제서야 시간제한이 있다는걸 알게 됐다
이대로 내면 100초에 끊을 경우 넘버링으로 다시 쓰는것도 안되기때문에 실제로 다 찾더라도 0이 될께 뻔했다
고로 이 코드는 못쓰는 코드라고 생각하고 다시 생각을 하기 시작했다.

다시 생각을 해보니까, 이런 생각이 들었다. 얘네를 왜 굳이 I/O를 이용해야 하는가?에 대한..
애초에 작은 사전을 만들었으니까, 모두 string 화 해서 사용하면 안될까? 생각이 들었다.
이때 stack overflow에서 찾은 이 글에서 영감을 많이 받았다.
요는, 파이썬은 루프는 느리다. 램에 다 올려서 string으로 찾아라. 그건 굉장히 빠르다.
그래서 덤프를 뜨고 메모리에서 비교를 해보기로 했다.
import timeit,re
from multiprocessing import Process
  
  
start_time = timeit.default_timer() #when program start

pass_dump = ""
hash_dump = ""


def init():
    global pass_dump, hash_dump
    f = open("out_c.out","r")
    pass_dump = f.read()

    f = open("aa.out")
    hash_dump = f.read()



def do_work():
   lines = hash_dump.split("\r\n")
   for i in lines:
    search_word(i.split(":")[2])
  
def search_word(word):
    match = re.search(r'.*'+word,pass_dump)
    if match:
        print match.group(0)
     
if __name__ == '__main__':

    init()

    do_work()

    stop_time = timeit.default_timer() #when program end

    print "Dictionary matching done"

    print "Numbering.."
    #numbering()
    #os.remove("Passwords_tmp.txt")
    print "Done!!"

    print "Program Run Time : " + str(stop_time - start_time)


요점은 한번에 싹 읽고, 정규식을 이용해 해당 라인을 다 가져와서 처리해보겠다고 한것이다.
정규식쓰면 당연히 빠르겠거니 하고 생각했는데, 어찌됐던 이전보단 빨라보이긴했는데 아니 끝이 안난다.
이 코드 검증은 안해서 잘 모르겠지만 그냥 망했다는 삘이 왔다. 너무 느렸다..

그래서 먼저 한 후배한테 물어보니까 그냥 딕셔너리 기본 옵션으로 구현했다고 한다.
무슨차이가 있을까.. 정규식보다 더 빠를 수 있을까? 속는 심정으로 코드를 짰다.
import timeit

start_time = timeit.default_timer() #when program start

pass_dump = {}
hash_dump = {}
count = 0

def init():
    global pass_dump, hash_dump
    with open("dictionary.out","r") as dic:
       for a in dic:
        key = a.split("|")[1].replace("\r\n","").replace("\n","") #hash
        value = a.split("|")[0] #original text
        pass_dump[key] = value

    with open("hashedPasswords.out","r") as dic:
       for a in dic:
        key = str(a.split(":")[2].replace("\r\n","").replace("\n","")) #hash
        value = a.split(":")[1] #user
        hash_dump[key] = value

    print "Init finished.."

def do_work():
    global hash_dump,pass_dump,count

    for a in hash_dump:
        try:
            if pass_dump[a]:
                count += 1
                fp = open("Passwords.txt","a")
                fp.write(str(count)+":"+hash_dump[a]+":"+pass_dump[a]+"\r\n")
                fp.close()
        except KeyError:
            pass
  
def search_word(word):
    match = re.search(r'.*'+word,pass_dump)
    if match:
        print match.group(0)
     
if __name__ == '__main__':

    init()
    do_work()

    stop_time = timeit.default_timer() #when program end

    print "Done!!"
    print str(count) + " Password(s) found. Recorded at Passwords.txt"
    print "Program Run Time : " + str(stop_time - start_time)


겉치레 이런거 없이, 이 코드는 그냥 엔터치면 검색이 끝났다...
배열처럼 사용하는 딕셔너리의 키값에 해쉬를 넣고 반환되는 값이 있나 없나 비교해보는것으로 모든 면에서 우수한것을 확인했다.
원리는 잘 모르겠다. 도대체 나는 동기화 이딴건 왜 찾아보고 있었나 이런 의문이 그냥 들었을뿐....
그냥 파이썬이 딕셔너리 키를 매칭해 찾는 속도가 개빠르구나.. 생각만 들었다. 혹시 구성할일 있으면 딕셔너리가 짱이니 잘쓰도록..

어쨌든, 쉽게 접근하면 쉽게 끝낼 수 있었던 과제였는데 너무 꼬아서 생각하는 바람에,
시간은 시간대로 잡아먹고 결과는... 안나왔지만 뻔할거라 생각한다만..

그래도 간만에 재밌는 과제였다 생각한다. 이런 짱구를 굴리는 과제가 재밌고 좋다.
이 개삽질이 누군가에게 도움되길 바라며 github 링크를 조심스럽게 링킹한다..



Reference
https://sites.google.com/site/reusablesec/Home/password-cracking-tools/probablistic_cracker
http://ko.wikipedia.org/wiki/%EC%97%AD%EC%83%81_%EA%B3%B5%EA%B2%A9
http://www.laurentluce.com/posts/python-and-cryptography-with-pycrypto/
http://www.xorbin.com/tools/sha256-hash-calculator
https://wiki.skullsecurity.org/Passwords
http://www.thegeekstuff.com/2014/07/advanced-python-regex/
http://stackoverflow.com/questions/8023306/get-key-by-value-in-dictionary
http://stackoverflow.com/questions/3449384/fastest-text-search-method-in-a-large-text-file

2015/03/26 11:50 2015/03/26 11:50

2014년

Gossip 2014/12/31 04:11
다시 돌아오지 않을 이시간. 2014년의 12월 31일.
네이버 블로그에 글을 쓸 수도 있겠다만 써놔봐야 찾기도 힘들 것 같고 그냥 여기 적어볼까한다.
회고와 다짐을 같이..

직장

2014년 4월에, 직장을 그만 두었다.
병역특례를 기다리고 있다가 법이 바뀌어서인데, 2013년 12월 9일에 발표난 것 치고 4달이나 더 다닌것 같다.
2013년~ 2014년 회사다니면서 작업한 홈페이지로는..

2013. 04?~ surem int'l intranet (내부 홈페이지로 따로 접근할 수 없음)
2013. 09 ~ 10 surem.net (대외 홈페이지로서 특별한 기능은 없다)
2013. 10 ~ 12 discountyourcost.com (이 프로젝트는 중지되었는데 아직 있다;; 친구 스타트업?을 도와준다고 간단히 만든 홈)
2014. 02 ~ 03 lute.fantazm.net (기존 싴갤러스에서 새갤러스로 모든 기능을 이관하고 디자인을 다시했다)
2014. 04 ~ 05 m.surem.com 기존 surem.com 의 모바일 버전으로 jquery-mobile을 사용했다. 풀로 쓰진않았고 맛만..
2014. 05.       harp,mand,wolf

여러 홈페이지를 만든것 같다 꽤.. 생각나는건 일단 저 정도인것 같다.
2014년에만 한건 아니지만, 회사가 classic ASP를 사용해서 이에 대한 코딩도 좀했고(surem.net 이 기존 asp로 짜여진 홈페이지여서, 포팅하기 위해 어떻게 하다보니 알게 되었다), ASP를 더 이상 사용하지 않는다고 해서 JSP를 회사에 도입하기도 했다.

surem.net은 혼자 작업해서 따로 툴을 쓸 필욘 없었지만,
m.surem.com을 작업할때는 정말 제대로 협업을 해야했기때문에 이때 처음 SVN과 Jenkins를 사용해서 형상관리와 배포를 했다.
(회사내에서 협업이라는 개념이 없어서.... 개발자는 각자의 파트만 혼자 개발하기때문에 같이 코딩할일이 잘 없어서 그런것 같다.. 정작 내 프로젝트도 버저닝을 안하고 있다.. 안습..)
또한, 회사 보안 문제로 mod_security와 webknight라는 죽어도 볼일 없을 것 같은 방화벽들을 체험하기도 했다.

그리고 회사에서 사용되는 PHP 모듈도 어느정도 다시 확고히 작성했고 새 기능도 추가했고..

coryjin이라는 넘사벽을 경험하면서 node.js 의 맛을 보게 되었고, 나에게 있어서 공부할 계기와 실제로 공부를 꽤 많이 한 시절이었다.
회사가 솔직히 어렵진 않고 학교 놀러가는 기분이어서 가볍게 다닌것 같다. 일이 좋았다는 건 아니고, 업무 처리 프로세스 같은건 좀 개선되었었으면.... 하는 생각을 많이 했다. 나에게 있어선 좋은 추억이 된 회사로 남았다. 끝마무리를 이렇게 깔끔하고 좋게 하는 사람이 잘 없는데....ㅋㅋ


방학

생각해보면 3학년 방학때부터 따로 방학이란게 없었다.
정확히 말하자면 집에 잘 없었다. 기사였나 산업기사 시험 날짜를 놓쳐서 김재환네 집에 도망가서 일주일 살기도했고(2013년 얘긴아니다), 3학년 겨울방학엔 대전에서, 4학년 여름방학은 현근집에서 몰래 숨어살았고, 겨울방학 시작하기도 전에 취직을 해서 나와 살았다.

직장을 그만두고 나서, 대학원을 준비했는데, 사실 도피의 의미가 좀 많이 컸으나 안되면 말고라고 생각도 많이 했다.
성균관대학교, 시립대학교 대학원을 썼고 전부 다 붙었다.  사실 연세대나 고려대에 쓰고 싶었는데 직장을 4월에 그만두고 알아봤었는데 연,고대는 3월에 원서를 쓴단다... 그래서 5월에 두 학교 면접을 보고 집에 내려갔었다.

집에서 7 8월까지의 시간을 보냈는데 솔직히 말하자면 정말 잉여롭게 보낸것 같다.
몸과 마음이 그렇게 늘어지기도 쉽지가 않은데, 어쨌든 알바를 구해서 알바도 다니고 그랬고, 당연히 군대가야되는 줄 알고 있던 어머니와 아빠에게 대학원 가겠다고 폭탄 선언을 한 기간이다.

이때 별로 특별한 기억은 없고, 나한테 집이나 좀 물려주셨으면 하는 바람을 가지게 되....긴했다.

기술적으로는 이때 mysql replication을 안정화 시켰다.
시작은 슈어엠때 하긴했는데, db 튜닝같은건 이 기간내에 잉여라서 많이했고, redis도 본격적으로 이 기간에 도입해서 사용했다.


대학원

그리고 9월부터 학교에 다니기 시작했고, 정신없는 석달을 보내고 지금이다.
지금은 보안을 공부한다는데.... 솔직히 연구실을 너무 대충알아봤음에도 제대로 된 연구실에 온것 같아 좋다.
뭣보다 사람들이 그렇게 악한 사람들이 없어서.. 그리고 직장-> 학교로 다시 오니 일을 대강대강하는게 눈에 보이기도한다
그래도 회사가 더 까다롭긴한것 같다. 학교는 그래도 학생의 신분이라서....

TCP/IP 및 다른 보안을 메이저로 하여 공부하고 있고, 웹 해킹쪽으로 파고 있는데 솔직히 웹해킹은 인젝션외엔 너무 뻔해서 (아직 선무당일수도있다), 사례 같은걸 보면서 공부하고 있고, python을 익히고 있다.
또 bro라는 ids를 좀 심도있게 파고있고...... 뭐 그렇다. 대학원 와서는 특별히 언어적으로 뭐 익히고 하진 못한 것 같다.
그나마 파이썬을 사용시작하게되고 다뤄보고 있다는게 소득이라면 소득..?

나는 보안도 좋지만 보안을 베이스로 하는 개발자가 되고자 한다.
바쁘다는 핑계가 아니라 정말 너무 바빠서 아직 손을 못대고, 2014년을 보내주려 한다.

2015

논문

논문은 두세편 정도 쓰려고 한다. 그 이상은 쓸 주제가 있을진 잘 모르겠는데, 아무래도 웹 관련해서 주제선정하고 쓰는데 많은 시간을 보내게 될 것 같은 해이다. 2014년 한학기 동안 논문을 안써서.... 논문에는 많은 시간을 투자해야될 것 같다. 괜찮다. 1년은 기니까.
주제는 웹으로 꾸준히 밀 생각인데, 글쎄 웹 관련 논문부터 많이 읽어봐야겠지
도서는 딱 오십권을 읽는걸 목표로 하자.

개발

역시 개발이 주제에서 빠질 수 없는데, 조금 장황한 계획이 있다.
웹은 계속해서 할 생각이고, 생각나는데로 적어보자면

python, ruby, scala
node.js + framework - meteor나 express 등
PHP - framework 체험도 체험이고, 가을에 나올 7.0도 개발해보고 싶다. 또한, 싴갤러스 클래스화를 진행하고..
c# - c#은 정말 한다한다 하는데 하질 못하는 그런녀석이다. 꼭 해보고 싶다.
java - android 개발자 등록하고, 푸시앱을 왕창 만들어보고 싶다. 다 개인용도로 쓰는거지....

그리고 git와 github을 이용해서 버저닝을 할 계획이고, 전체적인 언어의 깊이를 주고 싶은 한해가 될 것 같다.
너무 많은 계획이 아닌가 할 수 있겠지만 어느정도는 다 해본 녀석들이라... 새로운 도전은 없어뵌다.

파이썬 루비 스칼라는 마스터는 못해도 개념은 반드시 알고 넘어갈 계획이고 python같은경우는 flask나 django를 이용해서 서비스 하나를 만들어보는게 목표다. 크던 작던 일단 만들어야 뭘 할 것 같다.

운동

그리고 운동을 할려고 한다. 반 강제로 시작하게 될 것 같지만, 건강이 적신호인건 내가 누구보다 잘 알고 있다
몸뚱아리도.... 좀 깔끔하게 살기 위해서라도 운동을 해서 움직이는 습관을 들여야할 것 같다.
학교 헬스장을 이용하면 좋을 것 같고, 구체적인 계획을 세워서 강제로라도 시간 맞춰 운동해볼 계획이다. 한시간만이라도..

좀 장황한 회고와 앞으로의 계획을, 2014년이 끝나기 1시간 30분전에 마무리하면서,
새로이 다짐을 한번 해본다. 힘든 한해 잘 이겨낸 나에게 용기를 주며, 이 글을 읽는 모두가 행복하길 바란다!
2014/12/31 04:11 2014/12/31 04:11
블로그도 다 꾸며놓고, 첫 글도 썼는데, 2번째 글을 뭘로 써야하나 고민하게 되었다.
왠지 용두사미가 될 것 같은데 어떻게 유익한 글을 써보나..(믿지 못하겠지만 잡글 없는 클린 블로그를 지향... 첫 글이 좀 그렇지만 일단 목표는 그렇다), 고민하다가 Momentum(이하 모멘텀) 이라는 녀석을 알게 되었다.

모멘텀은 진짜 별 거 없는 'new tab' extension 이다.
새로운 탭을 열면 아래 화면이 뜬다......

구글 크롬 사용자는 여기서 다운로드 받을 수 있다.

크롬에 먼저 설치를 해보고, 사진이 바뀌는거에 꽤 만족했는데, 파이어폭스는 준비하고 있다고만 하고 아직 부가기능탭에 나오지 않았다. 사실 요즘 느끼는건데 파이어폭스 참 어려운 시기인것 같다. 부가기능도 크롬이 이제 더 뛰어난것 같고, 개발자들도 파이어폭스보단 사용자가 많은 크롬의 손을 들어주는 것 같다.

어쨌든, 구현자체는 어려워보이지 않았다. html 소스였으니까.. 그냥 파일 따다가 쓰면 될 것 같았다.
하지만 사람은 귀찮음의 생물, 처음엔 소스보기를 해서 일일히 손크롤을 할까 하다가, github를 뒤지기 시작했다..
덕중의 덕은 양덕 성님이라고 했던가, 역시 있었다. Mcdo - Momentum Flickr
아마 이 프로젝트는 소스를 그대로 사용하는데, 사진이 좀 많이 변하길 원하는 그런 느낌의 프로젝트였다.
어쩄든 이걸 써볼까 했는데, 플리커가 자체가 너무 느려서 ... 써먹진 못할것 같았다.

그래서 그냥 원본을 따기로했다.
역시 손크롤 할 수 있지만 왜 그러지 않느냐? 귀찮으니까..
파이어폭스는 xpi라는걸로 관리되는데, 크롬도 그런게 있을 것 같아서 보니 crx로 관리된다고 한다.
crx는 어떻게 얻는지 아직은 잘 모르겠고, 그냥 따주는데가 있다.

crx를 얻고 확장자를 zip으로 변경 후에 압축을 풀면 압축이 풀린다.
근데 놀라운 사실은, 알집으로는 안풀린다.. 7zip으론 풀리는데.. 알약의 기술력에 혀를 한번 내둘렀다.
이렇게 momentum의 소스를 얻었고 이제 xpi로 옮기면 될터.

그런데........xpi는 어떻게 만들지?
구글에 어떻게 만들지? 입력해보니 여러개가 검색되는데, github에서는 어떻게 개발하고 있을까 궁금했다.
사실 구글에 나와있는 한글 블로그들 보면 거의 2009년글들이고 깃헙에서 공유되고 있는 소스와 그냥 구조자체가 완전히 판이했다.
그래서, 뭔가 다른 방법이 있을거라 생각했고, 다른 방법은 있었다.

http://stackoverflow.com/questions/20409349/what-is-the-easiest-way-to-develop-firefox-extension
이 스택 오버 플로우 글을 보면, 자세히 소개되어 있다.
mozila-build 라는 툴을 제공하고 이를 이용해서 쉽게 만들 수 있다는 것 이다.
눈뒤집히는 소리가 아닐 수 없다. install.rdf 를 바꿔야된다느니, guid 를 따로 적어야된다느니 소개한 블로그들 글을 왜 보고 따라하고 있었나 이런 생각이 들었다.

답글에 너무 잘 설명이 되어있기때문에, 설치과정을 따로 설명하진 않겠다.
리눅스, 윈도우 둘다 mozila-build를 제공해주는데, 개인적인 느낌으로는 리눅스쪽에 훨씬 더 친숙하게 되어 있는것 같다.
세팅이랄것도 없는 세팅을 하고나고, cfx init을 하니 github에서 본 extension의 뼈대들이 생성되었다.



생성된 구조는 역시 대세를 따르듯이 js였다.
프로젝트를 생성했으니, 튜토리얼을 따르면 될 터, 튜토리얼을 켜고 만들어 보기 시작했다.
리눅스 운영체제에서 사용하는 사람은 cfx run이나 test는 좀 어려울것 같고, packing을 위해서 cfx xpi 를 하면 알아서 다 패킹된다는것에 너무너무 편안함을 느꼈다 -_-;

테스트를 순조로히 마치고 data에 모두 우겨넣고 xpi를 깔아봤는데 이게 왠일, 하나도 안보인다..
이상해서 이것저것 확인해보니 크롬에선 잘 돌아가는데, 파이어폭스에서는 안돌아가는 기이한 현상 발생..
opacity 이슈인가해서 css를 훑어보니 임의로 수정해주니 나오긴하는데, 중앙정렬도 제대로 안되고 엉망이었다.

쉽게 갈 수 있을 줄 알았던 것에서 터지니까, 괜히 파이어폭스 익스텐션이 없던게 아니구나 하는 생각이 들어서 아차 싶었다.
여기까지 했는데 물러날 순 없지 했으나..
너무 늦은 시간 시작했던터라 시간이 5시를 가까이 가리키는 것을 보고 블로그에 (1)을 붙이고, 2편에서 다시 써보기로 한다..
아마 어떤 점을 수정하고, 어떻게 변경하겠다는 js 변경이 주가 되지 않을까 하는데..

글이 너무 길어지는 감이 있으니, 적당히 분량조절하는 것 같아 괜찮은 기분이다.
2편에서 계속!!
2014/12/26 02:04 2014/12/26 02:04
누가 말했던가, 시험기간에는 공부외엔 모든 능률이 200%가 된다고....

나도 그 법칙을 피해갈 수 없었으니, 블로그 디자인을 뚝딱 해내버리고 말았던 것이었던 것이었던 것이었다...

깔끔하고 좋다. 개인적으론 만족하고 있다. 저 위에 검색창은 일부러 머티리얼을 안줬고..

강조한점:
1. 구글 material design 을 사용하려고 함. bootstrap도 좋지만 이쪽이 더 깔끔한 느낌을 강조하는데 좋을 것 같았음. 이를 합쳐놓은 프로젝트를 Github에서 발견했다. (bootstrap 이 아닌 일반 material ui를 만들고 있는 쪽도 있다.)
2. 사이드바를 모조리 지워버림. 트랙백은 또 뭐야. 블로그에 집중해!
3. 노인이 봐도 한눈에 잘 보이도록 글자크기를 개무식하게 설정함.
4. 참고한 웹(텍스트큐브)도 있다. 항상 참고하는 아웃사이더님에게서 영감을 얻었다. 사실 전혀 달라보이지만 글씨 크기라던지 배열이라던지..

혹시 모르겠지만 이 스킨에 관심있는 사람이 있을 수도 있으니 github 에 올려둔다.
참고로 이스킨은 모든 리소스를 cdn으로 갖다 쓰기때문에 폴더만 만들고 넣으면 그대로 스킨으로 쓸 수 있다 (...)
2014/12/17 23:11 2014/12/17 23:11