def U09(): report = pm.openReport('U-09.txt') pm.printTitle(report, '[U-09] /etc/hosts 파일 소유자 및 권한 설정') status = stat('/etc/hosts') isRightOwner = False owner = status.st_uid if owner == 0: isRightOwner = True pm.printNotice(report, '/etc/hosts 파일의 소유자가 root 입니다.') else: pm.printWarning(report, '/etc/hosts 파일의 소유자가 root가 아닙니다.') isRightPerm = False perm = int(oct(status.st_mode)[-3:]) if perm == 600: isRightPerm = True pm.printNotice(report, '/etc/hosts 파일의 권한이 600 입니다.') else: pm.printWarning(report, '/etc/hosts 파일의 권한이 600이 아닙니다.') if isRightOwner and isRightPerm: pm.printSafe(report) else: pm.printNotsafe(report) pm.printSolution(report, '[U-09] 조치 방법') pm.printSolution(report, '\t/etc/hosts 파일의 소유자를 root로 권한을 600으로 변경하세요.') pm.printSolution(report, '\t\t#chown root /etc/hosts') pm.printSolution(report, '\t\t#chmod 600 /etc/hosts\n') report.close()
def U07(): report = pm.openReport('U-07.txt') pm.printTitle(report, '[U-07] /etc/passwd 파일 소유자 및 권한 설정') status = stat('/etc/passwd') isRightOwner = False owner = status.st_uid if owner == 0: isRightOwner = True pm.printNotice(report, '/etc/passwd 파일의 소유자가 root 입니다.') else: pm.printWarning(report, '/etc/passwd 파일의 소유자가 root가 아닙니다.') isRightPerm = False perm = int(oct(status.st_mode)[-3:]) if perm <= 644: isRightPerm = True pm.printNotice(report, '/etc/passwd 파일의 권한이 644 이하입니다.') else: pm.printWarning(report, '/etc/passwd 파일의 권한이 644 이하가 아닙니다.') if isRightOwner and isRightPerm: pm.printSafe(report) else: pm.printNotsafe(report) pm.printSolution(report, '[U-07] 조치 방법') pm.printSolution(report, '\t/etc/passwd 파일의 소유자를 root로 권한을 644로 변경하세요.') pm.printSolution(report, '\t\t#chown root /etc/passwd') pm.printSolution(report, '\t\t#chmod 644 /etc/passwd\n') report.close()
def U05(): report = pm.openReport('U-05.txt') pm.printTitle(report, '[U-05] root 홈, 패스 디렉터리 권한 및 패스 설정') pm.printNotice(report, 'U-05의 점검은 현재 계정의 PATH만을 점검합니다.') envList = subprocess.check_output('echo $PATH', shell=True) envList = str(envList) isNotDot = False if '.' in envList: pm.printNotsafe(report, 'PATH에 \".\"이 포함되어 있습니다.') else: isNotDot = True isNotColon = False if '::' in envList: pm.printNotsafe(report, 'PATH에 \":\"이 포함되어 있습니다.') else: isNotColon = True if isNotDot and isNotColon: pm.printSafe(report) else: pm.printNotsafe(report) pm.printSolution(report, '[U-05] 조치 방법') pm.printSolution(report, '\t환경 설정 파일을 확인 및 수정하세요.') pm.printSolution(report, '\t\t점검이 필요한 파일들') pm.printSolution(report, '\t\t/etc/environment') pm.printSolution(report, '\t\t/etc/profile') pm.printSolution(report, '\t\t/etc/profile.d/*.sh') pm.printSolution(report, '\t\t~/.profile') pm.printSolution(report, '\t\t~/.bashrc') #pm.printSolution(report, '\t(수정 전) PATH=\".:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games\"\n') #pm.printSolution(report, '\t(수정 후) PATH=\"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games\"\n') pm.printSolution(report, '\t\".\" 혹은 \":\"을 제거\n') report.close()
def U03(): report = pm.openReport('U-03.txt') pm.printTitle(report, '[U-03] 계정 잠금 임계값 설정') isSafe = False isSet = False # pam_tally2로 수정 tocheck = 'auth required pam_tally2.so' tocheck = ''.join(tocheck.split()) # KISA 매뉴얼에는 system-auth로 되어 있음 # 우분투는 common-auth f = open('/etc/pam.d/common-auth', 'r') for line in f: line = ''.join(line.split()) # 공백 제거 if tocheck in line: # 주석인지 확인 if '#' in line: continue index = line.find('deny=') if index < 0: continue index += len('deny=') limit = '' while line[index] > '0' and line[index] < '9': limit = limit.join(line[index]) index += 1 if int(limit) > 5: pm.printWarning(report, '계정 잠금 임계값이 5 초과입니다.') pm.printWarning(report, '현재 임계값은 ' + limit + ' 입니다.') isSet = True else: pm.printNotice(report, '현재 임계값은 ' + limit + ' 입니다.') isSet = True isSafe = True f.close() if not isSet: pm.printWarning(report, '계정 잠금 임계값이 설정되어 있지 않습니다.') if isSafe: pm.printSafe(report) else: pm.printNotsafe(report) pm.printSolution(report, '[U-03] 조치 방법') pm.printSolution(report, '\t계정 잠금 임계값을 5이하로 설정하세요.') pm.printSolution( report, '\t\t텍스트 에디터를 이용하여 \"/etc/pam.d/common-auth\" 파일을 엽니다.') pm.printSolution(report, '\t\t아래의 내용을 수정 또는 추가하세요.') pm.printSolution(report, '\t\t순서가 중요하므로 수행문 최상단에 입력해.') pm.printSolution(report, '\t\tauth required pam_tally2.so ') pm.printSolution( report, '\t\t\tonerr=fail deny=5 unlock_time=120 no_magic_root') pm.printSolution(report, '\t\t no_magic_root : root에게는 패스워드 잠금 설정을 적용하지 않음') pm.printSolution(report, '\t\t deny=5 : 5회 입력 실패 시 계정 잠금') pm.printSolution( report, '\t\t unlock_time : 계정 잠김 후 설정 시간이 지나면 잠김 해제 (단위: 초)\n') report.close()
def U49(): report = pm.openReport('U-49.txt') pm.printTitle(report, '[U-49] 불필요한 계정 제거') pm.printNotice(report, 'U-49의 점검은 U-49.txt를 참고하여 관리자가 직접 수행바랍니다.\n') pm.printSolution(report, '[U-49] 조치 방법') pm.printSolution(report, '\t"/etc/passwd\" 파일을 확인하여') pm.printSolution(report, '\t미사용 계정, 의심스러운 계정, 사용하지 않지만') pm.printSolution(report, '\t기본적으로 생성되는 계정들을 확인합니다.') pm.printSolution(report, '\t불필요한 사용자 계정은 제거합니다.') pm.printSolution(report, '\t\t# userdel <user_name>\n') report.close()
def U51(): report = pm.openReport('U-51.txt') pm.printTitle(report, '[U-51] 계정이 존재하지 않는 GID 금지') pm.printNotice(report, 'U-51의 점검 및 조치는 시스템 관리자와 검토하여야 합니다.') pm.printNotice(report, 'U-51.txt를 참고하여 관리자와 사용자가 직접 수행바랍니다.') pm.printSolution(report, '[U-51] 조치 방법\n') pm.printSolution(report, '\t계정이 존재하지 않고 시스템 운영에 사용되지 않는 그룹 등') pm.printSolution(report, '\t불필요한 그룹이 존재하는 경우 해당 그룹을 제거하세요.') pm.printSolution(report, '\t\t\"/etc/group\" 파일과 \"/etc/passwd\" 파일을 같이 확인하여') pm.printSolution(report, '\t\t불필요한 그룹을 확인') pm.printSolution(report, '\t\t# groupdel <group_name>\n') report.close()
def U53(): report = pm.openReport('U-53.txt') pm.printTitle(report, '[U-53] 사용자 shell 점검') names = ['daemon', \ 'bin', \ 'sys', \ 'adm', \ 'games', \ 'listen', \ 'nobody', \ 'nobody4', \ 'noaccess', \ 'diag', \ 'operator', \ 'gopher'] f = open('/etc/passwd', 'r') isNologin = True for line in f: splitLine = line.split(':') name = splitLine[0] if name in names: shell = splitLine[6] # KISA 매뉴얼에는 /sbin/nologin 이지만 우분투는 /usr/sbin/nologin 이다. if ( shell.find('/usr/sbin/nologin') < 0 ) \ and ( shell.find('/bin/false') < 0 ) \ and ( shell.find('/sbin/nologin') < 0 ): isNologin = False pm.printWarning(report, name + ' 계정의 로그인이 가능합니다.') pm.printNotice( report, '일반적으로 UID 1000 미만 60000 이상의\n\t 시스템 계정(sync 제외)의 확인이 필요합니다.') if isNologin: pm.printSafe(report) else: pm.printNotsafe(report) pm.printSolution(report, '[U-53] 조치 방법') pm.printSolution(report, '\t로그인이 필요하지 않은 계정에 대해 nologin 쉘로 변경하세요.') pm.printSolution(report, '\t\t텍스트 에디터를 이용하여 \"/etc/passwd\" 파일을 엽니다.') pm.printSolution(report, '\t\t해당 계정의 항목의 맨 마지막 필드인 로그인 쉘을.') pm.printSolution(report, '\t\t우분투 기준으로 \"/usr/sbin/nologin\"으로 변경하세요.\n') report.close()
def U06(): report = pm.openReport('U-06.txt') pm.printTitle(report, '[U-06] 파일 및 디렉터리 소유자 설정') pm.printNotice(report, 'U-06의 점검은 모든 파일을 검사하므로 상당한 시간이 소요됩니다.') pm.printNotice(report, '조치 방법을 참고하여 사용자가 직접 수행바랍니다.\n') pm.printSolution(report, '[U-06] 조치 방법') pm.printSolution(report, '\t소유자가 존재하지 않는 파일 및 디렉터리를 삭제하거나 소유자를 변경하세요.') pm.printSolution(report, '\t수정이 필요한 파일을 아래의 명령어로 검색합니다.') pm.printSolution(report, '\t\t# find / -nouser -print') pm.printSolution(report, '\t\t# find / -nogroup -print') pm.printSolution(report, '\tfind 뒤의 최상위 디렉터리 \"/\" 대신 필요한 경로를 입력하셔도 됩니다.') pm.printSolution(report, '\t대상 파일 혹은 디렉터리가 불필요할 경우에는 삭제합니다.') pm.printSolution(report, '\t필요한 경우에는 chown으로 소유자를 변경합니다.') pm.printSolution(report, '\t\t# chown <user_name> <file_name>\n') report.close()
def U10(): report = pm.openReport('U-10.txt') pm.printTitle(report, '[U-10] /etc/(x)inetd.conf 파일 소유자 및 권한 설정') filename = '' isInetd = path.isfile('/etc/inetd.conf') if not isInetd: pm.printNotice(report, '/etc/inetd.conf 파일이 없습니다.\n') else: filename = '/etc/inetd.conf' isXinetd = path.isfile('/etc/xinetd.conf') if not isXinetd: pm.printNotice(report, '/etc/xinetd.conf 파일이 없습니다.\n') else: filename = '/etc/xinetd.conf' if (not isInetd) or (not isXinetd): report.close() return status = stat(filename) isRightOwner = False owner = status.st_uid if owner == 0: isRightOwner = True pm.printNotice(report, filename + ' 파일의 소유자가 root 입니다.') else: pm.printWarning(report, filename + ' 파일의 소유자가 root가 아닙니다.') isRightPerm = False perm = int(oct(status.st_mode)[-3:]) if perm == 600: isRightPerm = True pm.printNotice(report, filename + ' 파일의 권한이 600 입니다.') else: pm.printWarning(report, filename + ' 파일의 권한이 600이 아닙니다.') if isRightOwner and isRightPerm: pm.printSafe(report) else: pm.printNotsafe(report) pm.printSolution(report, '[U-10] 조치 방법') pm.printSolution(report, '\t' + filename + ' 파일의 소유자를 root로 권한을 600으로 변경하세요.') pm.printSolution(report, '\t\t#chown root ' + filename) pm.printSolution(report, '\t\t#chmod 600 ' + filename) # xinetd.d 의 하위도 검사하도록 수정 필요 if isXinetd: pm.printSolution(report, '\t/etc/xinetd.d 디렉터리 하위의 취약한 파일도 동일한 방법으로 조치하세요.') report.close()
def U46(): report = pm.openReport('U-46.txt') pm.printTitle(report, '[U-46] 패스워드 최소 길이 설정') isSafe = False isSet = False f = open('/etc/login.defs', 'r') for line in f: line = ''.join(line.split()) index = line.find('PASS_MIN_LEN') if index >= 0: # 주석인지 확인 if '#' in line[0:index]: continue passLen = line[index + len('PASS_MIN_LEN'):] if passLen == '': continue elif int(passLen) < 8: pm.printWarning(report, '패스워드 최소 길이가 8자 미만입니다.') pm.printWarning(report, '현재 최소 길이는 ' + passLen + '자 입니다.') isSet = True else: pm.printNotice(report, '현재 패스워드 최소 길이는 ' + passLen + '자 입니다.') isSet = True isSafe = True f.close() if not isSet: pm.printWarning(report, '패스워드 최소 길이가 설정되어 있지 않습니다.') if isSafe: pm.printSafe(report) else: pm.printNotsafe(report) pm.printSolution(report, '[U-46] 조치 방법') pm.printSolution(report, '\t패스워드 최소 길이를 8자 이상으로 설정하세요.') pm.printSolution(report, '\t공공기관인 경우 9자 이상으로 설정하세요.') pm.printSolution(report, '\t\t텍스트 에디터를 이용하여 \"/etc/login.defs\" 파일을 엽니다.') pm.printSolution(report, '\t\t아래의 내용을 수정 또는 추가하세요.') pm.printSolution(report, '\t\t PASS_MIN_LEN 8\n') report.close()
def U48(): report = pm.openReport('U-48.txt') pm.printTitle(report, '[U-48] 패스워드 최소 사용기간 설정') isSafe = False isSet = False f = open('/etc/login.defs', 'r') for line in f: line = ''.join(line.split()) index = line.find('PASS_MIN_DAYS') if index >= 0: # 주석인지 확인 if '#' in line[0:index]: continue days = line[index + len('PASS_MIN_DAYS'):] if days == '': continue elif int(days) < 1: pm.printWarning(report, '패스워드 최소 사용기간이 1일 미만 입니다.') pm.printWarning(report, '현재 최소 사용기간은 ' + days + '일 입니다.') isSet = True else: pm.printNotice(report, '현재 패스워드 최소 사용기간은 ' + days + '일 입니다.') isSet = True isSafe = True f.close() if not isSet: pm.printWarning(report, '패스워드 최소 사용기간이 설정되어 있지 않습니다.') if isSafe: pm.printSafe(report) else: pm.printNotsafe(report) pm.printSolution(report, '[U-48] 조치 방법') pm.printSolution(report, '\t패스워드 최소 사용기간을 1일로 설정하세요.') pm.printSolution(report, '\t\t텍스트 에디터를 이용하여 \"/etc/login.defs\" 파일을 엽니다.') pm.printSolution(report, '\t\t아래의 내용을 수정 또는 추가하세요.') pm.printSolution(report, '\t\t PASS_MIN_DAYS 1 (단위: 일)\n') report.close()
def U50(): report = pm.openReport('U-50.txt') pm.printTitle(report, '[U-50] 관리자 그룹에 최소한의 계정 포함') isSafe = True f = open('/etc/group', 'r') for line in f: splitLine = line.split(':') group = splitLine[0] # KISA 매뉴얼에는 root 그룹만 검사함 # 난 sudo 그룹도 추가하였다. if group == 'root': users = splitLine[3].split(',') numOfUsers = len(users) if '\n' in users: numOfUsers -= 1 if 'root' in users: numOfUsers -= 1 if numOfUsers > 0: pm.printWarning(report, 'root 그룹에 불필요한 계정이 존재합니다.') isSafe = False elif group == 'sudo': users = splitLine[3].split(',') numOfUsers = len(users) if '\n' in users: numOfUsers -= 1 if 'sudo' in users: numOfUsers -= 1 if numOfUsers > 0: pm.printNotice(report, 'sudo 그룹에 ' + str(numOfUsers) + '개의 계정이 존재합니다.') pm.printNotice(report, '사용자가 직접 계정 현황 확인 후 조치 바랍니다.') f.close if isSafe: pm.printSafe(report) else: pm.printNotsafe(report) pm.printSolution(report, '[U-50] 조치 방법') pm.printSolution(report, '\t관리자 그룹에 불필요한 계정은 제거하세요.') pm.printSolution(report, '\t\t# deluser <user_name> root\n') report.close()
def U54(): report = pm.openReport('U-54.txt') pm.printTitle(report, '[U-54] Session Timeout 설정') # check bash shell = subprocess.check_output('echo $SHELL', shell=True) shell = str(shell) if not ('bash' in shell): pm.printNotice(report, 'bash shell이 아닙니다.') report.close() return echoRst = subprocess.check_output('echo $TMOUT', shell=True) echoRst = str(echoRst) startIdx = echoRst.find('\'') endIdx = echoRst.find('\\n') time = echoRst[startIdx + 1:endIdx] isTimeout = False if time == '': pm.printWarning(report, 'Session Timeout이 설정되어 있지 않습니다.') elif int(time) > 600: pm.printWarning(report, 'Session Timeout이 10분 이상으로 설정되어 있습니다.') else: isTimeout = True if isTimeout: pm.printSafe(report) else: pm.printNotsafe(report) pm.printSolution(report, '[U-54] 조치 방법') pm.printSolution(report, '\tSession Timeout을 10분 이하로 설정하세요.') pm.printSolution(report, '\t\t텍스트 에디터를 이용하여 \"~/.bashrc\" 파일을 엽니다.') pm.printSolution(report, '\t\t아래의 내용을 수정 또는 추가하세요.') pm.printSolution(report, '\t\t TMOUT=600') pm.printSolution(report, '\t\t export TMOUT\n') report.close()
def U45(): report = pm.openReport('U-45.txt') pm.printTitle(report, '[U-45] root 계정 su 제한') isSafe = False tocheck = 'auth required pam_wheel.so' tocheck = ''.join(tocheck.split()) f = open('/etc/pam.d/su', 'r') for line in f: line = ''.join(line.split()) if tocheck in line: # 주석인지 확인 if not ('#' in line): isSafe = True f.close() if isSafe: pm.printNotice(report, '특정 그룹만 su 명령어를 사용할 수 있습니다.') pm.printSafe(report) else: pm.printWarning(report, '모든 사용자가 su 명령어를 사용할 수 있습니다.') pm.printNotsafe(report) pm.printSolution(report, '[U-45] 조치 방법') pm.printSolution(report, '\t특정 그룹의 사용자만 su 명령어를 사용하도록 제한시키세요.') pm.printSolution(report, '\t\twheel 그룹이 존재하지 않을 시 wheel 그룹을 생성하세요.') pm.printSolution(report, '\t\t # groupadd wheel') pm.printSolution(report, '\t\twheel 그룹에 su 명령어를 사용할 사용자를 추가하세요.') pm.printSolution(report, '\t\t usermod -G wheel <user_name>') pm.printSolution(report, '\t\t텍스트 에디터를 이용하여 \"/etc/pam.d/su\" 파일을 엽니다.') pm.printSolution(report, '\t\t아래와 같이 주석을 제거하거나 설정하세요.') pm.printSolution(report, '\t\t auth sufficient pam_rootok.so') pm.printSolution( report, '\t\t auth required pam_wheel.so debug group=wheel\n') report.close()
def U08(): report = pm.openReport('U-08.txt') pm.printTitle(report, '[U-08] /etc/shadow 파일 소유자 및 권한 설정') isShadow = path.isfile('/etc/shadow') if not isShadow: pm.printNotice(report, '/etc/shadow 파일이 없습니다.') report.closed() return status = stat('/etc/shadow') isRightOwner = False owner = status.st_uid if owner == 0: isRightOwner = True pm.printNotice(report, '/etc/shadow 파일의 소유자가 root 입니다.') else: pm.printWarning(report, '/etc/shadow 파일의 소유자가 root가 아닙니다.') isRightPerm = False perm = int(oct(status.st_mode)[-3:]) if perm == 400: isRightPerm = True pm.printNotice(report, '/etc/shadow 파일의 권한이 400 입니다.') else: pm.printWarning(report, '/etc/shadow 파일의 권한이 400이 아닙니다.') if isRightOwner and isRightPerm: pm.printSafe(report) else: pm.printNotsafe(report) pm.printSolution(report, '[U-08] 조치 방법') pm.printSolution(report, '\t/etc/shadow 파일의 소유자를 root로 권한을 400으로 변경하세요.') pm.printSolution(report, '\t\t#chown root /etc/shadow') pm.printSolution(report, '\t\t#chmod 400 /etc/shadow\n') report.close()
def U02(): report = pm.openReport('U-02.txt') pm.printTitle(report, '[U-02] 패스워드 복잡성 설정') pm.printNotice(report, '기존에 설정 되어 있는 패스워드에 대해서는 점검할 수 없습니다.') isSafe = False output = subprocess.getoutput('dpkg -l | grep libpam-pwquality') if not ('libpam-pwquality' in output): pm.printWarning(report, 'pwquality.conf 파일이 존재하지 않습니다.') pm.printWarning(report, 'libpam-pwquality를 설치해 주세요.') pm.printWarning(report, '# apt install libpam-pwquality') else: # pam_pwquality로 수정 tocheck = 'password requisite pam_pwquality.so' tocheck = ''.join(tocheck.split()) f = open('/etc/pam.d/common-password', 'r') for line in f: line = ''.join(line.split()) # 공백 제거 if tocheck in line: # 주석인지 확인 if '#' in line: continue isSafe = True # minlen index = line.find('minlen=') if index < 0: pm.printWarning(report, '최소 길이가 설정되어 있지 않습니다.') else: index += len('minlen=') minlen = '' while line[index] > '0' and line[index] < '9': minlen = minlen.join(line[index]) index += 1 if int(minlen) < 8: pm.printWarning(report, '최소 패스워드 길이가 8 미만입니다.') pm.printWarning(report, '현재 최소 길이는 ' + minlen + ' 입니다.') isSafe = False else: pm.printNotice(report, '현재 최소 길이는 ' + minlen + ' 입니다.') # lcredit index = line.find('lcredit=-1') if index < 0: pm.printWarning(report, '소문자 요구가 설정되어 있지 않습니다.') isSafe = False else: pm.printNotice(report, '소문자 요구가 설정되어 있습니다.') # ucredit index = line.find('ucredit=-1') if index < 0: pm.printWarning(report, '대문자 요구가 설정되어 있지 않습니다.') isSafe = False else: pm.printNotice(report, '대문자 요구가 설정되어 있습니다.') # dcredit index = line.find('ucredit=-1') if index < 0: pm.printWarning(report, '숫자 요구가 설정되어 있지 않습니다.') isSafe = False else: pm.printNotice(report, '숫자 요구가 설정되어 있습니다.') # ocredit index = line.find('ucredit=-1') if index < 0: pm.printWarning(report, '특수문자 요구가 설정되어 있지 않습니다.') isSafe = False else: pm.printNotice(report, '특수문자 요구가 설정되어 있습니다.') f.close() if isSafe: pm.printSafe(report) else: pm.printNotsafe(report) pm.printSolution(report, '[U-02] 조치 방법') pm.printSolution(report, '\tlibpam-pwquality 패키지를 설치하세요.') pm.printSolution(report, '\t\t# apt install libpam-pwquality') pm.printSolution(report, '\t패스워드 복잡성 설정 파일의 내용을 내부 정책에 맞도록 수정하세요.') pm.printSolution( report, '\t\t텍스트 에디터를 이용하여 \"/etc/pam.d/common-password\" 파일을 엽니다.') pm.printSolution(report, '\t\t아래의 내용으로 수정하세요.') pm.printSolution( report, '\t\tpassword requisite pam_pwquality.so retry=3 minlen=8 ') pm.printSolution(report, '\t\t\tlcredit=-1 ucredit=-1 dcredit=-1 ocredit=-1') pm.printSolution(report, '\t\t lcredit=-1 : 소문자 최소 1자 이상 요구') pm.printSolution(report, '\t\t ucredit=-1 : 대문자 최소 1자 이상 요구') pm.printSolution(report, '\t\t dcredit=-1 : 숫자 최소 1자 이상 요구') pm.printSolution(report, '\t\t ocredit=-1 : 특수문자 최소 1자 이상 요구') pm.printSolution(report, '\t\t minlen=8 : 최소 8자리 이상 요구') pm.printSolution(report, '\t\t retry=3 : 3번 재입력 가능\n') report.close()