본문 바로가기
(실기) 정보보안기사&산업기사

UNIX/Linux 기본 학습 21~25

by IT매니절 2024. 3. 14.

*syslog 설정 및 관리
syslog API(표준 인터페이스) - 응용 프로그램들, 커널들이 API를 이용해 로깅하고 syslogd(로그 데몬)이 요청을 받아 syslog.conf 설정파일을 참조하여 로그파일을 남기거나 콘솔에 뿌려주거나, 외부서버Remote Server에 전송
syslog(priority, "%s", "로그문자열")

syslog.conf 파일 구조
facility.prioroty; facility.prioroty; ··· action(logfile-location)
       A.B                     A.B                             C

ex

local7.*                                /var/log/test.log

=> local7의 facility에, 모든* 로그를 test.log라는 이름으로 남기겠다는 뜻

(최근에는 rsyslog /etc/rsyslog.conf 사용

설정을 변경한 후에는 service rsyslogd restart 필요)

 

syslog는 정보보호 특성을 고려하지 않고 개발되었다
UDP(514 포트)를 통해 로그를 전송할 때 공격자가 syslog 메시지를 모니터링해 중요 정보를 알아낼 수 있다
TCP를 이용하도록 권고. payload를 보호할 수 있는 BEEP를 이용하도록 권고

 

facility : 로그 생성 서비스
* 모든 서비스
auth, authpriv 인증, 보안 메시지
cron
daemon
kem
syslog
user

local7 시스템 부팅 메시지 기록용도로 씀
등등...

 

prioroty : 로그 수준 (level)

1. Emergency (emerg) 전면중단되는 패닉panic상태, 전체공지필요. System is unusable
2. Alert (alert) 즉각적인 조치가 필요한 상황 (db오류) action must be laken immediately
3. Critical (crit) 하드웨어 등의 심각한 오류 critical condition
4. Error (err) 일반적인 에러/오류 error condition
5. Warming (warming) 경고 warming condition
6. Notice 에러는 아닌데 관리자의 조치 필요 nomal, but significant condition
7. Infomation 의미있는 정보 메시지 infomation message
8. debug 디버깅용

* 순서대로 암기 필요 이얼크데웜노인디 EACEWNID

syslog.conf에 로그수준을 *로 지정하면 모든 로그 수준의 로그를 남긴다
none 로그를 남기지 않음
로그 수준을 warming으로 지정하면, 그 위의 Error, Critical, Alert, Emergency 전부 다 남음

 

//c언어로 로그남길때 예시
#include <syslog.h>

openlog("syslog_test", LOG_PID, LOG_LOCAL1);
//openlog로 syslog 선언

syslog(LOG_EMERG, msg, strlen(msg));
syslog(LOG_ALERT, msg, strlen(msg));
syslog(LOG_CRIT, msg, strlen(msg));
syslog(LOG_ERR, msg, strlen(msg));
...


closelog();
//open - close 세트

 

action 로그를 어디에 남길 것인가
로그파일 : ex /var/log/test
콘솔 : /dev/console
원격로그 서버 : @호스트주소 ex @192.168.0.0
user : 지정된 사용자의 스크린 ex root
* : 현재 로그인된 모든 사용자의 스크린

 

(보안관제 (ESM, SLEM)
실무적으로 모든 syslog를 수집용 syslog서버에 모아서 관제함)

 

실습
1. conf 파일에 UDP 514 포트를 열어서 준비
2. 남길 로그 action에 @192.168.0.0 원격 syslog서버 호스트 주소 적어줌 (데몬 재시작)
3. 514포트 rsyslogd 열렸는지 확인 (netstat -anup | grep 514)
4. hostname으로 /var/log/secure에 로그 남았는지 확인
(서버 두 개가 필요해서 일단 영상을 보기만 했음)

 

syslog.conf 예시
1. #kern.*                   /dev/console              -> 주석처리됨. 모든 레벨, 콘솔에 남김.
2. *.info;mail.none;    /var/log.messages     -> 모든서비스에 대한 info 수준이상 로그, 메일은 none
3. *.emerg                 *                                 -> 모든 서비스에 대한 emerg 수준 이상 로그를 모든 사용자 스크린에 전송

 

 

-- 21 END

 

 

로그 모니터링
텍스트파일 모니터링시 tail -f /var/log/messages 명령을 이용하면 유용함 (-f는 실시간으로 추가되는 내용들 출력)

utmp, wtmp, lastlog 같은 바이너리 형식 로그들은 별도의 명령어를 사용해 확인해야함

 

로그파일 순환rotate 관리
- logrotate는 시스템 로그파일을 관리하기 위한 도구
- 사이즈가 너무 커지면 디스크 사용률이 100%가 되어 시스템 장애 발생
로그파일 순환, 압축(compress) 등의 기능을 가짐

/usr/sbin/logrotate          데몬 위치
/etc/logrotate.conf           데몬 설정파일
/etc/logrotate.d               logrotate를 적용할 프로세스/데몬 설정파일 (디렉토리)
/var/lib/logrotate.status   작업내역 보관
/etc/cron.daily/logrotate  cron에 의해 일단위로 실행

 

/etc/logrotate.conf 구성요소


1. weekly                                 -> 로그 파일 순환주기 (주단위) 그외 daily, monthly
2. rotate 4                               -> 순환 로그파일의 개수를 4개로 지정 (현재 주단위니까, 4주 보관)
3. create                                  -> 순환시 새로 파일 만듦 create 777 root root 퍼미션 소유자 소유그룹 지정가능
4. dateext                                -> 날짜로 확장자를 주겠다 date + ext (ex access_log-20180414)
5. #compress                          -> =nocompress 가능
6. include /etc/logrotate.d        -> 해당 디렉터리의 설정파일을 참조하여 logrotate 적용

7. size n                                   -> 지정한 크기가 되면 로그파일 순환 ex size 10M

 

/etc/logrotate.d 디렉터리 안에 각각 서비스들에 대한 설정들이 있음


missingok : 로그파일이 없어도 오류를 발생시키지 않는다
notifempty : not if empty. 로그파일이 비어있는 경우 순환하지 않음 <-> ifempty 빈파일이어도 순환
sharedscripts : 로그파일이 여러개 있어도 prerotate, postrotate 스크립트를 한 번만 실행
postrotate~endscript : 순환 후 스크립트 파일 실행

delaycompress 첫 순환때 말고, 다음 순환때 압축

 

postrotate~endscript 이 사이에 자주 들어가는 내용

/bin/kill -HUP `cat /var/run/httpd/~.pid 2>/dev/null` 2> /dev/null | true
=> 서버를 종료하지 않으면서도 설정파일을 다시 읽을수 있는 방법은 SIGHUP
~.pid 파일을 읽어서 -HUP 명령을 날려 종료하지 않으면서 재실행 할 수 있도록 함

 

-- 22 END

 

Stack 스택 (LIFO Last - in First - out)
후입선출, 가장 마지막에 입력된 것이 가장 먼저 출력

 

프로그램 동작방식
- 레지스터 Register 정보 (CPU가 연산을 수행하기 위해 사용하는 고속 저장장치)
1) ESP Extended Stack Pointer 
   스택 프레임의 최하위 포인터를 저장
2) EBP Extended Base Pointer
   스택 프레임에 기준이 되는 SFP포인터 저장. EBP 기준 상대주소로 프레임 내 변수에 접근
3) EIP Extended Instruction Pointer
   다음에 실행할 명령어의 주소값 저장

 

프로세스 메모리 레이아웃 (UNIX)

[high address]

STACK
- 함수 처리, 로컬 변수나 매개변수처리
상위주소->하위주소로 할당



HEAP
-동적 메모리 할당 영역
하위주소->상위주소로 할당
BSS
(Block Started by Symbol)
초기화 안된 전역변수, 정적변수
Initialized DATA
- 초기화된 전역 변수, 전역변수
TEXT (Code Aera)
- 기계어 실행코드가 위치하는 영역. 어셈블리어

[low address]

 

 

프로그램 동작 방식

Code Area                         Stack Area


void func(){
 int b;           <- 0x30     
}

int main() {   <-START
 int a;           
 func();         <- 0x10
 return 0;      <- 0x20
}
high

low

 

1. START : Entry Point 시작점

2. int a

ESP - int a의 아래 위치인 0x1020 -> 0x101C로 이동 (호출로 인한 할당)
EBP - int a의 아래 위치인 0x1020
EIP - 다음코드 func();가 있는 0x10

3. func();

EIP는 다음줄 return 0;가 있는 0x20의 주소지 값을 갖는다.

=> 그런데 다음줄로 넘어가기 전에 func() 내부를 먼저 수행해야 하므로, 0x20을 push EIP로 백업한다

(push EIP = stack의 값을 증가시켜 ESP를 이동시킨다)

=> ESP의 값이 0x1018로 이동하며, 0x20의 주소지를 백업(RET: Return address)

=> JUMP 명령어로 EIP 값은 int b; 가 있는 0x30을 갖는다

=> int main() -> func()로 넘어갈때 프롤로그 Prologue (호출된 함수의 stack frame을 구성하는 과정) 수행

=> 프롤로그를 수행할때 Push EBP로 stack값이 증가되며 ESP의 값이 0x1014로 이동된다

=> Push EBP로 base point(이전 함수의 EBP이자 SFP 0x1020)를 0x1014에 백업

=> mov EBP ESP          ESP 값을 EBP에 할당. 즉 EBP는 0x1014가 된다

4. int b;

ESP가 int b; 를 위해 0x1010으로 이동한다 (호출로 인한 할당)

=> 에필로그 Epilogue (이전에 호출한 함수의 stack frame 복원하는 과정) 수행

=> leave 명령 수행 (mov ESP EBP -> pop EBP)  EBP를 ESP로 이동시킨다. ESP = 0x1014

=> EBP가 pop되어 0x1014에 저장했던 이전함수의 주소지. 0x1020 주소지를 가져온다 EBP = 0x1020

=> pop 되면서 ESP도 같이 이동되어, ESP는 0x1018 주소지에 있음

=> pop EIP 명령어 실행. EIP는 0x20을 다시 갖는다

(과정은 기사 출제범위를 벗어날 듯 하지만 알아두면 좋은)

 

명령어 push 값/레지스터
=> SFP 변경, 값 저장

명령어 pop 레지스터
=> 현재스택 포인터가 위치한 지점에서 값을 꺼내 레지스터에 저장시키다

 

요약하자면

PUSH로 백업하고 (프롤로그)

POP으로 백업을 꺼내온다 (에필로그) 라는 느낌

 

에필로그 과정에서 pop EIP 할 때 악성코드의 위치로 Return Address를 조작하는것 = 버퍼 오버플로우

 

 

-- 23 End

 

 

 

버퍼 오버플로우를 이해하기 위한 C 코드
1) strcpy(char *dst, const char *src)                       <- 취약한 함수
- dst에 src를 저장. src 문자열 길이를 체크하지 않아서, dst 버퍼크기를 초과할 수 있음
2) strncpy(char *dst, const char *src, size t len)     <-안전한 함수
- src 문자열의 len 만큼만 dst 버퍼에 저장한다
3) size_t strlen(const char *s)
- null문자를 제외한 byte 수 반환
4) sizeof(피연산자)
- 피연산자 크기 반환

C언어에서 null(0x00)은 문자열의 끝을 표현
문자열 4개인 abcd = abcd\0 을 의미하여 5byte 차지함

 

호출 : ./basic "인자값1" "인자값2"
basic.c
int main(int argc, char **argv){
 ....
}

argc : 전달되는 매개변수의 개수. 프로그램명+인자값개수 (인자로 2개 주어지면 +1 해서 총 3개임)
**argv : 인자를 참조

ex argv[0]=프로그램명인 basic, argv[1]=첫번째 매개변수인 인자값1, argv[2] ...

 

memset(버퍼 변수, 0x00, sizeof(버퍼변수));
=> 버퍼변수를 0x00으로 버퍼변수 크기만큼 초기화

 

printf에서 %x : 헥사코드로 출력

 

strcpy 함수로 버퍼를 초과하는 인자값을 설정하여 stack buffer overflow를 발생시킨다
=> 버퍼를 초과시켜 Return Address까지 넘쳐나게 해 조작할 수 있다

 

buffer 사이즈가 5일때.
정상실행 ($buf_over, "AAAA")
buffer에는 AAAA
SFP에 이전 함수의 SFP
RET에 이전 함수의 EIP

공격 ($buf_over `perl -e 'print "A"x7, "\x12\x34\x56\x78"`')
buffer에는 AAAAA
SFP에는 AA                  <- 7개의 A가 buffer를 넘쳐 SFP까지 침범
RET에는 0x78563412   <- RET까지 침범한 악성코드 주소 (역순기록)

 

(짤막지식 `perl -e 'print ~'` print를 실행하겠다는 뜻)

 

실행파일을 디버깅 할 수 있는 툴 : gdb
ex gdb ./buf_over
(gdb) disas main
=> ./buf_over파일의 main의 어셈블리 코드를 보여준다

(gdb) info functions
=> ./buf_over파일의 함수 주소값들이 나온다
또는
(gdb) p buf_attck 이런식으로 함수명 쳐서 확인도 가능

 

void buf_attck(){                      //root 권한 탈취용 공격함수

 setreuid(0, 0);        -> ruid, euid를 root로 지정
 setregid(0, 0);        -> rgid, egid를 root로 지정
 system("/bin/sh");  -> root 및 root그룹 권한으로 쉘 실행
}


./buf_over의 파일에 넣어둔, 공격 함수 buf_attck 의 주소를 확인하고(ex 0x12345678),
buf_over `perl -e 'print "A"x9, "\x78\x56\x34\x12"`' 이렇게 주소는 역순으로 표기해 실행
buffer = 5개의 A
SFP = 4개의 A
RET (pop EIP) = 공격함수의 주소 0x12345678 가 들어감

 

(짤막지식
Endian 에디언
2Byte 이상의 자료형의 데이터를 표현하는 순서
          0x12345678
<-상위 byte    하위byte->
Intel cpu 
- Little Endian 하위바이트를 먼저 메모리에 쓴다 0x78563412
- Big Endian 상위바이트를 먼저 메모리에 쓴다 0x12345678 (보통 서버장비))

 

버퍼 오버플로우 방지 대책
- strcp 대신 strncp를 사용한다
ex strncp(buffer 변수, argv[1], sizeof(버퍼변수)-1) 
- 조건문IF를 통해 방지

ex if(strlen(argv[1]) >= sizeof(buffer 변수)){          = strlen(argv[1]) > sizeof(buffer)-1
  printf("너무 크게 입력했다는 안내문");
  exit(1);
}

 

스택 버퍼 오버플로우
- 문자열 계산 등에 의해 정의된 버퍼의 한계치를 넘을 때
힙 버퍼 오버플로우
- malloc() 등을 이용할때, 최초 정의된 힙의 메모리 사이즈를 초과하여 문자열이 저장될 때

 

스택 버퍼 오버플로우 대응기술
1. 스택가드 stack guard
 - 복귀주소와 변수사이에 특정 값을 저장해두었다가 그 값이 변경되면 변조로 판단하고 중단시킴
2. 스택쉴드 stack shield
 - 복귀주소를 Global RET라는 특수 스택에 저장해두고 함수종료시 비교하여 다르면 중단시킴
3. ASLR Address Space Layout Randomization
 - 주소공간 배치를 난수화하고 메모리주소를 변경시킨다

 

 

-- 24 END

 

 

짤막지식

Instruction : cpu가 명령어를 실행하는 최소 단위

 

레이스 컨디션 Race Condition
- 둘 이상의 프로세스/스레드간에 공유자원에 접근했을때,
접근 순서에 따라 원하지 않는 결과/비정상적 결과가 발생할 수 있는 상태/조건
- 경쟁상태가 발생할 수 있는 코드 영역을 임계영역 Critical section 이라 함


Adder함수의 내용 var = var + 1 의 실행과정
var가 5일때, 프로세스 A가 먼저 Adder 수행
1. load 메모리 -> register       <- 로드할 때 Context switching이 발생하진 않음
2. add register 1 증가
3. store register -> 메모리     <- 이 때 B에게로 Context switching이 발생한다면?
B는 1이 증가된 6의 결과를 store하고
A가 1이 증가된 6의 결과를 store한다

Adder가 두 번 실행되었는데, 결과적으로는 1만 증가된 상태 = 비정상

Context switching (다른 프로세스로 CPU 제어권이 넘어감)

동기화 처리 Synchronization : 접근 순서 제어, 동시 접근 배제
ex Mutex, semaphore

 

레이스 컨디션 공격 Race Condition Attack
- 프로세스가 임시파일을 만들 경우,

실행 중에 끼어들어 임시파일을 목적파일로 연결(심볼릭 링크)하여 악의적 행위를 하는 것
- ex A 프로그램 실행 → 심볼릭 링크된 tmp.sh 오픈 -> 공격자가 /etc/shadow 파일에 대한 접근 
                                  ↑ (A프로그램이 setuid root 설정된 상태)
          tmp.sh -> /etc/shadow 심볼릭링크 시도

 

실습
1. setuid root로 설정된 test_attck 파일을 통해, tmp.dat(심볼릭링크 파일)과 변조할 데이터를 넣어 호출
2. 공격자가 tmp.dat -> /etc/shadow 심볼릭 링크 파일 생성
3. 같은 시각 test_attck 파일을 이용하려던 사용자A는 원본 tmp.dat이 아닌 심볼릭링크된 tmp.dat을 오픈하게 됨
4. /etc/shadow는 변조할 데이터로 덮어씌워짐

 

레이스 컨디션 대응방안
- 가능하면 임시파일을 생성하지 않는다
- 동일한 파일이 이미 존재할 경우 생성/쓰기 금지
- 사용할 파일에 심볼릭링크가 걸려있으면 실행을 중단한다
- umask를 022로 유지하여 임시파일이 공격자에 의해 악의적으로 삭제되지 않도록함 (디렉토리의 w권한 삭제)

 

 

 

-- 25 END