시스템 보안 운영 - 3 (교육 84일차)

SW·2023년 3월 29일
0
/lib/security : 32bit PAM 관련 모듈 디렉터리
/lib64/security : 64bit PAM 관련 모듈 디렉터리

[root@systemhacking ~]# ls /lib64/security
pam_access.so    pam_faildelay.so  pam_listfile.so    pam_pwhistory.so       pam_succeed_if.so  pam_unix_passwd.so
pam_cap.so       pam_faillock.so   pam_localuser.so   pam_pwquality.so       pam_systemd.so     pam_unix_session.so
pam_chroot.so    pam_filter        pam_loginuid.so    pam_rhosts.so          pam_tally2.so      pam_userdb.so
pam_console.so   pam_filter.so     pam_mail.so        pam_rootok.so          pam_time.so        pam_warn.so
pam_cracklib.so  pam_ftp.so        pam_mkhomedir.so   pam_securetty.so       pam_timestamp.so   pam_wheel.so
pam_debug.so     pam_group.so      pam_motd.so        pam_selinux.so         pam_tty_audit.so   pam_xauth.so
pam_deny.so      pam_issue.so      pam_namespace.so   pam_selinux_permit.so  pam_umask.so
pam_echo.so      pam_keyinit.so    pam_nologin.so     pam_sepermit.so        pam_unix.so
pam_env.so       pam_lastlog.so    pam_permit.so      pam_shells.so          pam_unix_acct.so
pam_exec.so      pam_limits.so     pam_postgresok.so  pam_stress.so          pam_unix_auth.so

[root@systemhacking ~]# ls /etc/pam.d/
chfn              fingerprint-auth-ac  password-auth-ac  runuser            smtp.postfix  sudo-i
chsh              login                polkit-1          runuser-l          sshd          system-auth
config-util       other                postlogin         smartcard-auth     su            system-auth-ac
crond             passwd               postlogin-ac      smartcard-auth-ac  su-l          systemd-user
fingerprint-auth  password-auth        remote            smtp               sudo          vlock

[root@systemhacking ~]# ls /etc/security/
access.conf   console.handlers  group.conf   namespace.conf  opasswd         sepermit.conf
chroot.conf   console.perms     limits.conf  namespace.d     pam_env.conf    time.conf
console.apps  console.perms.d   limits.d     namespace.init  pwquality.conf

실습> pam 소스 분석

# cd
# yum -y install bzip2 yum-utils flex-devel

컴파일 에러 때문에 flex-devel을 설치했는데 그래도 
에러가 발생되서 개발툴 전체를 설치한다.
# yum -y groupinstall "Development Tools"

# yumdownloader --downloadonly --source pam
# rpm -Uvh  pam-1.1.8-23.el7.src.rpm
# cd rpmbuild/SOURCES
# tar xjf Linux-PAM-1.1.8.tar.bz2
# cd Linux-PAM-1.1.8
# ./configure
# make
  <== 컴파일이 완료되면 실행파일들이 생긴다.

# tree -d -L 1
.
├── build-aux
├── conf
├── doc
├── examples <-- PAM을 이용하는 프로그램들의 샘플 소스가 저장되어 있다.
├── libpam
├── libpam_misc
├── libpamc
├── m4
├── modules  <-- 각 모듈이 위치한 디렉터리
├── po
├── tests    <-- PAM을 이용하는 프로그램들의 샘플 소스가 저장되어 있다.
└── xtests


테스트 파일을 확인한다.
# cat examples/check_user.c

실습> vimrc 설정하기

# vi /etc/vimrc
  :
  :(생략)
set nu
set ai
set ci
set bg=dark
set cursorline
set expandtab
set ts=4
set sw=4

# alias vi=vim

# vi ~/.bashrc
alias vi=vim  # 추가

실습> pam_rootok 분석하기

/root/rpmbuild/SOURCES/Linux-PAM-1.1.8/modules/pam_rootok

# pwd
/root/rpmbuild/SOURCES/Linux-PAM-1.1.8/
# cd
# ln -s /root/rpmbuild/SOURCES/Linux-PAM-1.1.8/modules .
# cd -P modules
# pwd
/root/rpmbuild/SOURCES/Linux-PAM-1.1.8/modules
# cd pam_rootok/

# vi pam_rootok.c

 42 #define mytest 1
 43  
 44 static int 
 45 _pam_parse (const pam_handle_t *pamh, int argc, const char **argv)
 46 {   

# make
# tree .libs/
.libs/
├── pam_rootok.la -> ../pam_rootok.la
├── pam_rootok.lai
├── pam_rootok.o    <-- 목적파일
└── pam_rootok.so   <-- 목적파일을 가지고 동적 라이브러리를 생성

0 directories, 4 files

# vi pam_rootok.c

 44 static int
 45 _pam_parse (const pam_handle_t *pamh, int argc, const char **argv)
 46 {
 47     int ctrl=0;
 48 
 49     printf("_pam_parse()\n");  // 출력하기 위해서 소스코드를 수정한다.
 50 
 51     /* step through arguments */
 52     for (ctrl=0; argc-- > 0; ++argv) {

# cp .libs/pam_rootok.so /usr/lib64/security/
cp: overwrite `/usr/lib64/security/pam_rootok.so'? y

SELinux 중지
# setenforce 0

# head -n 3 /etc/pam.d/su
#%PAM-1.0
auth		sufficient	pam_rootok.so
# Uncomment the following line to implicitly trust users in the "wheel" group.

# su -
_pam_parse()  <-- 소스코드에서 수정한 함수가 출력된다.
마지막 로그인: 금  3월 24 10:40:00 KST 2023 일시 pts/0
# exit


-- whileTest.c --
/*
 * 파일명: whileTest.c
 * 프로그램 설명: 반복문 while문 연습
 * 작성자: 리눅스마스터넷
 */

int main()
{
    int i = 1;              // 초깃값
    
    while (i <= 5)          // 조건식
    {
        printf("%d\n", i);  // 실행문
        i += 1;             // 증감식
    }

    return 0;
}
-- whileTest.c --

-- forTest.c --
/*
 * 파일명: forTest.c
 * 프로그램 설명: 반복문 for문 연습
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

int main()
{
    int i;

    // 초깃값;    조건식;   증감식
    for (i = 1; i <= 5; i += 1)          
    {
        printf("%d\n", i);  // 실행문
    }

    return 0;
}
-- forTest.c --

32bit용 라이브러리를 설치해야 -m32 옵션이 적용된다.
없으면 에러가 발생된다.
# gcc -Wall -m32 -g -o forTest forTest.c
# ./forTest
1
2
3
4
5

-g 옵션을 이용해서 컴파일을 해야 gdb를 이용해서 분석이 가능하다.
# gdb forTest
(gdb) b main
(gdb) r
(gdb) disp i
(gdb) n
(gdb) n
(gdb) n
(gdb) n
(gdb) n
(gdb) n
(gdb) n
(gdb) n
(gdb) n
(gdb) n
(gdb) n
18          return 0;
1: i = 6
(gdb) c
(gdb) q

Visual Studio 2022에서도 forTest.c 를 생성하여 분석한다.
만약 윈도우 디펜더가 exe 파일이 생성이되면 악성코드로 인식하고 삭제할 수 있으니
아래를 참고해서 exe가 만들어지는 솔루션 폴더를 예외처리를 설정한다.

윈도우 디펜더에서 예외처리하는 방법
https://cafe.naver.com/linuxmasternet/981

실습> argc, argv 사용하기

C언어서는 명령어도 인수의 개수 1개로 인식한다.

# vi argcTest.c
/*
 * 파일명: argcTest.c
 * 프로그램 설명: argc, argv 테스트
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

// int main()
// int main(int argc, char *argv[])
// int main(int argc, char **argv)
// int main(int argc, char **argv, char *envp[])
// int main(int argc, char **argv, char **envp)

int main(int argc, char **argv)
{
    // argc: argument count
    // argv: argument value, argument vector
    printf("argc: %d\n", argc);
    printf("argv[0]: %s\n", argv[0]);
    printf("argv[1]: %s\n", argv[1]);
    printf("argv[2]: %s\n", argv[2]);

    return 0;
}

# gcc -m32 -g -o argcTest argcTest.c
# ./argcTest
argc: 1
argv[0]: ./argcTest
argv[1]: (null)            <-- 없다는 의미
argv[2]: XDG_SESSION_ID=7  <-- 쓰레기값

# ./argcTest 111
argc: 2
argv[0]: ./argcTest
argv[1]: 111
argv[2]: (null)

# ./argcTest 111 222
argc: 3
argv[0]: ./argcTest
argv[1]: 111
argv[2]: 222

이 소스코드의 문제점은 인수가 3개 이상 들어와도 인식할 수 없다.
이유는 소스코드 3개까지만 하드코딩 되어 있어서 안된다.
그래서 이것을 인식하기 위해서는 argc 개수만큼 반복문을 이용해서 돌리면 된다.
# ./argcTest 111 222 333
argc: 4
argv[0]: ./argcTest
argv[1]: 111
argv[2]: 222


# ./argcTest 111 222 333 444 555
argc: 6
argv[0]: ./argcTest
argv[1]: 111
argv[2]: 222


# vi argcTest2.c
/*
 * 파일명: forTest.c
 * 프로그램 설명: 반복문 for문 연습
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

// int main()
// int main(int argc, char *argv[])
// int main(int argc, char **argv)
// int main(int argc, char **argv, char *envp[])
// int main(int argc, char **argv, char **envp)

int main(int argc, char **argv)
{
    // argc: argument count
    // argv: argument value, argument vector

    int i;

    printf("argc: %d\n", argc);

    for(i = 0; argc > 0; argc--)
    {
        printf("argv[%d]: %s\n", i, argv[i]);
        i++;
    }

    return 0;
}

# gcc -m32 -g -o argcTest2 argcTest2.c
# ./argcTest2
argc: 1
argv[0]: ./argcTest2

# ./argcTest2 11
argc: 2
argv[0]: ./argcTest2
argv[1]: 11

# ./argcTest2 11 22
argc: 3
argv[0]: ./argcTest2
argv[1]: 11
argv[2]: 22

# ./argcTest2 11 22 33
argc: 4
argv[0]: ./argcTest2
argv[1]: 11
argv[2]: 22
argv[3]: 33

# ./argcTest2 11 22 33 44
argc: 5
argv[0]: ./argcTest2
argv[1]: 11
argv[2]: 22
argv[3]: 33
argv[4]: 44

실습> 자료형

Shift + V > "ay: 레지스터 a에 블럭잡은 코드를 복사한다.
:reg  : 레지스터 확인
"aP   : 레지스터 a에 저장된 값을 복사한다.

1. 소스코드 작성
# vi dataType.c
/*
 * 파일명: dataType.c
 * 프로그램 설명: 자료형
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

// 형식: 자료형 변수명;
// 형식: 자료형 변수명 = 값;
int main()
{
    int i;

    i = 7;

    printf("i = %d\n", i);

    return 0;
}

2. 컴파일/실행
# gcc -m32 -g -o dataType dataType.c
# ./dataType
i = 7

3. gdb 분석
# alias gdb='gdb -q'
# vi .bashrc
  :
  :(생략)
alias vi='vim'
alias gdb='gdb -q'
alias gcc='gcc -Wall -m32 -g'

gdb로 실행파일 dataType을 분석한다.
# gdb dataType
Reading symbols from /root/dataType...done.
(gdb) b main
(gdb) r
(gdb) n
17          printf("i = %d\n", i);
(gdb) p i
$1 = 7
(gdb) p &i
$2 = (int *) 0xffffd62c
(gdb) x/xw &i
0xffffd62c:     0x00000007
(gdb) n
i = 7
19          return 0;
(gdb) c
(gdb) q

실습> 배열

1. 소스코드 작성
# vi array1.c
/*
 * 파일명: array1.c
 * 프로그램 설명: 배열
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

int main()
{
    int a[5];  // 배열의 이름은 메모리 주소다!
    // a == &a[0]
    // &a[0]

    a[0] = 1;
    a[1] = 2;
    a[2] = 3;
    a[3] = 4;
    a[4] = 5;

    printf("%d %d %d %d %d \n", a[0], a[1], a[2], a[3], a[4]);

    return 0;
}

# alias gcc
alias gcc='gcc -Wall -m32 -g'
# gcc -o array1 array1.c
# ./array1
1 2 3 4 5

# gdb array1
Reading symbols from /root/array1...done.
(gdb) b main
(gdb) r
13          a[0] = 1;
(gdb) n
14          a[1] = 2;
(gdb) n
15          a[2] = 3;
(gdb) n
16          a[3] = 4;
(gdb) n
17          a[4] = 5;
(gdb) n
19          printf("%d %d %d %d %d \n", a[0], a[1], a[2], a[3], a[4]);
(gdb) n
1 2 3 4 5
21          return 0;

(gdb) p a
$1 = {1, 2, 3, 4, 5}
(gdb) p &a
$2 = (int (*)[5]) 0xffffd61c
(gdb) x/xw &a
0xffffd61c:     0x00000001
(gdb) x/5xw &a
0xffffd61c:     0x00000001      0x00000002      0x00000003      0x00000004
                  | | | |         | | | |         | | | |         | | | |         
                  | | | +- 1c     | | | +-- 20    | | | +-- 24    | | | +-- 28
                  | | +-- 1d      | | +-- 21      | | +-- 25      | | +-- 29
                  | +-- 1e        | +-- 22        | +-- 26        | +-- 2a 
                  +-- 1f          +-- 23          +-- 27          +-- 2b   

0xffffd62c:     0x00000005
                  | | | |     
                  | | | +-- 2c
                  | | +-- 2d
                  | +-- 2e
                  +-- 2f
(gdb) p &a[0]
$3 = (int *) 0xffffd61c
(gdb) p &a[1]
$4 = (int *) 0xffffd620
(gdb) p &a[2]
$5 = (int *) 0xffffd624
(gdb) p &a[3]
$6 = (int *) 0xffffd628
(gdb) p &a[4]
$7 = (int *) 0xffffd62c

(gdb) c
(gdb) q

실습> 자료형2

# cat dataType2.c
/*
 * 파일명: dataType2.c
 * 프로그램 설명: 자료형
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

// 형식: 자료형 변수명;
// 형식: 자료형 변수명 = 값;
int main()
{
    // 문자 'A'
    // 문자 "Hello" <-- 컴파일 에러
    // 문자열  "Hello"
    char ch = 'A';  // ASCII 문자 1개만 저장

    printf("ch = %c\n", ch);

    return 0;
}

# gcc -o dataType2 dataType2.c
# ./dataType2
ch = A

# gdb dataType2
Reading symbols from /root/dataType2...done.
(gdb) b main
(gdb) r
16          char ch = 'A';
(gdb) n
18          printf("ch = %c\n", ch);
(gdb) n
ch = A
20          return 0;
(gdb) p ch
$1 = 65 'A'
(gdb) p &ch
$2 = 0xffffd62f "A@\204\004\b"  <-- 이상?
(gdb) x/xw &ch
0xffffd62f:     0x04844041  <-- 
                  | | | |     
                  | | | +-- 2f  <-- ch 변수
                  | | +-- 30  <-- 사용 안함 (쓰레기값으로 우리한테는 필요없는 값이다.)
                  | +-- 31  <-- 사용 안함 (쓰레기값 우리한테는 필요없는 값이다.)
                  +-- 32  <-- 사용 안함 (쓰레기값 우리한테는 필요없는 값이다.)
(gdb) c
Continuing.
[Inferior 1 (process 74839) exited normally]

-- dataType3.c --
/*
 * 파일명: dataType3.c
 * 프로그램 설명: 자료형
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

int main()
{
    char ch1 = 'A';
    char ch2 = 'B';
    char ch3 = 'C';
    char ch4 = 'D';

    printf("%c %c %c %c\n", ch1, ch2, ch3, ch4);

    return 0;
}
-- dataType3.c --
# gcc -o dataType3 dataType3.c
# ./dataType3
A B C D

# gdb dataType3
Reading symbols from /root/dataType3...done.
(gdb) b main
(gdb) r
11          char ch1 = 'A';
(gdb) n
12          char ch2 = 'B';
(gdb) n
13          char ch3 = 'C';
(gdb) n
14          char ch4 = 'D';
(gdb) n
16          printf("%c %c %c %c\n", ch1, ch2, ch3, ch4);
(gdb) n
A B C D
18          return 0;
(gdb) p &ch1
$1 = 0xffffd62f "Ap\204\004\b"
(gdb) p &ch2
$2 = 0xffffd62e "BAp\204\004\b"
(gdb) p &ch3
$3 = 0xffffd62d "CBAp\204\004\b"
(gdb) p &ch4
$4 = 0xffffd62c "DCBAp\204\004\b"
(gdb) x/xw &ch4
0xffffd62c:     0x41424344
                  | | | |     
                  | | | +-- 2c  <-- ch4 변수
                  | | +-- 2d  <-- ch3
                  | +-- 2e  <-- ch2
                  +-- 2f  <-- ch1
(gdb) c
(gdb) q

실습> 배열

동일한 자료형을 연속된 공간이 같이 묶어 놓은 것!
관리가 편하다.

아스키코드표
https://ko.wikipedia.org/wiki/ASCII

-- array2.c --
/*
 * 파일명: array2.c
 * 프로그램 설명: 배열
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

int main()
{
    char ch[6] = "ABCDE";

    printf("%c %c %c %c %c\n", ch[0], ch[1], ch[2], ch[3], ch[4]);
    printf("%s\n", ch);

    return 0;
}
-- array2.c --
# gcc -o array2.c array2.c
# ./array2
A B C D E
ABCDE

# gdb array2
Reading symbols from /root/array2...done.
(gdb) b main
Breakpoint 1 at 0x8048448: file array2.c, line 11.
(gdb) r
11          char ch[6] = "ABCDE";
(gdb) n
13          printf("%c %c %c %c %c\n", ch[0], ch[1], ch[2], ch[3], ch[4]);
(gdb) n
A B C D E
14          printf("%s\n", ch);
(gdb)
ABCDE
16          return 0;

ch 변수의 주소를 출력한다.
(gdb) p &ch
$1 = (char (*)[6]) 0xffffd62a

ch변수(배열) 의 주소부터 16진수(x)형태로 워드크기(w, 4byte) 만큼 2개를 출력한다.
- 배열의 이름은 메모리 주소이므로 가능하다.
(gdb) x/2xw ch
0xffffd62a:     0x44434241      0xd0000045

ch변수의 주소부터 16진수(x)형태로 워드크기(w, 4byte) 만큼 2개를 출력한다.
(gdb) x/2xw &ch
0xffffd62a:     0x44434241      0xd0000045

ch[0]방의 주소부터 16진수(x)형태로 워드크기(w, 4byte) 만큼 2개를 출력한다.
(gdb) x/2xw &ch[0]
0xffffd62a:     0x44434241      0xd0000045

(gdb) x/1xb &ch[0]
0xffffd62a:     0x41
(gdb) x/1xb &ch[1]
0xffffd62b:     0x42
(gdb) x/1xb &ch[2]
0xffffd62c:     0x43
(gdb) x/1xb &ch[3]
0xffffd62d:     0x44
(gdb) x/1xb &ch[4]
0xffffd62e:     0x45
(gdb) x/1xb &ch[5]
0xffffd62f:     0x00
(gdb) c
Continuing.
[Inferior 1 (process 74904) exited normally]
(gdb) q

실습> 배열

리눅스에서 ASLR(Address Space Layout Randomization) 주소 랜덤화 기능을 Off 시키기
ASLR On : 프로그램이 실행될때마다 실행되는 메모리 주소가 변경됨
ASLR Off: 프로그램이 실행될때마다 실행되는 메모리 주소가 고정됨
커널 파라미터: 
/proc/sys/kernel/randomize_va_space : 0 이면 Off
/proc/sys/kernel/randomize_va_space : 1 이면 On
/proc/sys/kernel/randomize_va_space : 2 이면 On

# echo 0 > /proc/sys/kernel/randomize_va_space

-- array2.c --
/*
 * 파일명: array2.c
 * 프로그램 설명: 배열
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

int main()
{
    char ch[6] = "ABCDE";

    // printf 서식 문자열
    // %c: 문자, %s: 문자열, %d: 정수, %p: 메모리 주소
    printf("%c %c %c %c %c\n", ch[0], ch[1], ch[2], ch[3], ch[4]);
    printf("%s\n", ch);
    printf("%p %p %p\n", ch, &ch, &ch[0]);

    return 0;
}
-- array2.c --
# gcc -o array2 array2.c
# ./array2
A B C D E
ABCDE
0xffffd65a 0xffffd65a 0xffffd65a

# ./array2
A B C D E
ABCDE
0xffffd65a 0xffffd65a 0xffffd65a

# ./array2
A B C D E
ABCDE
0xffffd65a 0xffffd65a 0xffffd65a

# ldd array2
        linux-gate.so.1 =>  (0xf7fd9000)
        libc.so.6 => /lib/libc.so.6 (0xf7e06000)
        /lib/ld-linux.so.2 (0xf7fda000)
# ldd array2
        linux-gate.so.1 =>  (0xf7fd9000)
        libc.so.6 => /lib/libc.so.6 (0xf7e06000)
        /lib/ld-linux.so.2 (0xf7fda000)
# ldd array2
        linux-gate.so.1 =>  (0xf7fd9000)
        libc.so.6 => /lib/libc.so.6 (0xf7e06000)
        /lib/ld-linux.so.2 (0xf7fda000)

# gdb array2
(gdb) b main
(gdb) r
11          char ch[6] = "ABCDE";
(gdb) n
13          printf("%c %c %c %c %c\n", ch[0], ch[1], ch[2], ch[3], ch[4]);
(gdb) n
A B C D E
14          printf("%s\n", ch);
(gdb) n
ABCDE
15          printf("%p %p %p\n", ch, &ch, &ch[0]);
(gdb) n
0xffffd62a 0xffffd62a 0xffffd62a
17          return 0;
(gdb) p &ch
$1 = (char (*)[6]) 0xffffd62a
(gdb) x/2xw &ch
0xffffd62a:     0x44434241      0xd0000045
(gdb) x/2xw ch
0xffffd62a:     0x44434241      0xd0000045
(gdb) x/2xw &ch[0]
0xffffd62a:     0x44434241      0xd0000045
(gdb) x/2xw 0xffffd62a
0xffffd62a:     0x44434241      0xd0000045
(gdb) c
(gdb) q

실습> 포인터

-- pointer1.c --
/*
 * 파일명: pointer1.c
 * 프로그램 설명: 포인터
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

int main()
{
    int a = 1;

    printf("a: %d, &a: %p\n", a, &a);

    return 0;
}

# ./pointer1
a: 1, &a: 0xffffd65c
-- pointer1.c --

# gcc -o pointer1 pointer1.c

# ./pointer1
a: 1, &a: 0xffffd65c


-- pointer1.c --
/*
 * 파일명: pointer1.c
 * 프로그램 설명: 포인터
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

int main()
{
    int a = 1;
    int *p;

    p = &a;

    printf("*&a: %d, a: %d, &a: %p\n", *&a, a, &a);
    printf(" *p: %d, p: %p, &p: %p\n", *p,  p, &p);

    return 0;
}
-- pointer1.c --

# gcc -o pointer1 pointer1.c
# ./pointer1
*&a: 1, a: 1, &a: 0xffffd65c
 *p: 1, p: 0xffffd65c, &p: 0xffffd658

-- pointer1.c --
/*
 * 파일명: pointer1.c
 * 프로그램 설명: 포인터
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

int main()
{
    int a = 1;
    int *p;

    p = &a;

    printf(" *&a: %d, a: %d, &a: %p\n", *&a, a, &a);
    printf("  *p: %d, p: %p, &p: %p\n", *p,  p, &p);
    printf("**&p: %d\n", **&p);

    return 0;
}
-- pointer1.c --

# gcc -o pointer1 pointer1.c
# ./pointer1
 *&a: 1, a: 1, &a: 0xffffd65c
  *p: 1, p: 0xffffd65c, &p: 0xffffd658
**&p: 1

gdb로 포인터 분석하기
# gdb pointer1
Reading symbols from /root/pointer1...done.
(gdb) b main
(gdb) r
(gdb) n 5
 *&a: 1, a: 1, &a: 0xffffd62c
  *p: 1, p: 0xffffd62c, &p: 0xffffd628
**&p: 1
20          return 0;

a변수에 저장된 값을 보여준다.
(gdb) p a
$1 = 1

a변수의 주소값을 보여준다.
(gdb) p &a
$2 = (int *) 0xffffd62c

p변수에 저장된 값을 보여준다.
(gdb) p p
$3 = (int *) 0xffffd62c

p변수의 주소값을 보여준다.
(gdb) p &p
$4 = (int **) 0xffffd628

(gdb) x/xw &a   <-- x/xw a 는 에러 (이유는 a는 주소가 아니기 때문이다.)
0xffffd62c:     0x00000001

변수 p에 저장된 값(a변수의 주소)을 따라가서 그 안에 저장된 값을 보여준다.
- 1이 출력한다.
(gdb) x/xw p
0xffffd62c:     0x00000001

p변수의 주소안에 저장된 값을 출력한다.
(gdb) x/xw &p
0xffffd628:     0xffffd62c  <-- a변수의 주소값

0xffffd62c: a변수의 주소이므로 a변수에 저장된 값 1을 출력한다.
(gdb) x/xw 0xffffd62c
0xffffd62c:     0x00000001

0xffffd628: p변수의 주소이므로 p변수에 저장된 값인 a의 주소를 출력한다.
(gdb) x/xw 0xffffd628
0xffffd628:     0xffffd62c

*0xffffd628: 0xffffd628 메모리 주소에 저장된 값( a변수의 주소 0xffffd62c)을 따라가서 그 안에 저장된 값을 출력한다. 
그러므로 1이 출력된다.
(gdb) x/xw *0xffffd628
0xffffd62c:     0x00000001

a의 주소를 출력한다.
(gdb) p &a
$7 = (int *) 0xffffd62c

a의 주소에 저장된 값을 출력한다.
(gdb) p *&a
$8 = 1

a의 주소에 저장된 값을 출력한다.
(gdb) x/xw 0xffffd62c
0xffffd62c:     0x00000001

a의 주소에 저장된 값을 출력한다.
(gdb) x/xw &a
0xffffd62c:     0x00000001

p주소에 저장된 값을 출력한다.
(gdb) x/xw &p
0xffffd628:     0xffffd62c  <-- a의 주소

p에 저장된 주소에 값을 출력한다.
(gdb) x/xw p
0xffffd62c:     0x00000001  <-- a의 값

(gdb) c
(gdb) q


# ./pointer2
 ch: AAA
&ch[0]: AAA

p: AAA
p: AAA

실습> 배열과 포인터

-- pointer2.c --
/*
 * 파일명: pointer2.c
 * 프로그램 설명: 포인터
 * 작성자: 리눅스마스터넷
 */

#include <stdio.h>

int main()
{
    char ch[4] = "AAA";
    char *p;

    p = ch;

    printf(" ch: %s\n", ch);
    printf("&ch[0]: %s\n", &ch[0]);

    printf("\n");

    printf("p: %s\n", p);
    printf("*&p: %s\n", *&p);

    return 0;
}
-- pointer2.c --

# gcc -o pointer2 pointer2.c
# ./pointer2
 ch: AAA
&ch[0]: AAA

p: AAA
*&p: AAA

gdb로 pointer2를 분석한다.
# gdb pointer2
Reading symbols from /root/pointer2...done.
(gdb) b main
(gdb) r
(gdb) n 7
 ch: AAA
&ch[0]: AAA

p: AAA
*&p: AAA
24          return 0;

(gdb) p ch
$1 = "AAA"
(gdb) p &ch
$2 = (char (*)[4]) 0xffffd62c
(gdb) p &ch[0]
$3 = 0xffffd62c "AAA"
(gdb) x/xw ch
0xffffd62c:     0x00414141

ch 변수의 크기를 확인한다.
p sizeof(변수)
(gdb) p sizeof(ch)
$4 = 4
(gdb) p sizeof(p)
$5 = 4
(gdb) p p
$6 = 0xffffd62c "AAA"
(gdb) x/xw p
0xffffd62c:     0x00414141
(gdb) x/xw &p
0xffffd628:     0xffffd62c
(gdb) c
(gdb) q


원격으로 PC를 제어하는 프로그램
- 구글에서 애니데스크를 검색해서 다운로드 받는다.


OpenSSH 패키지도 인증은 PAM을 이용한다.
[root@localhost Linux-PAM-1.1.8]# grep -i 'Usepam' /etc/ssh/sshd_config 
# WARNING: 'UsePAM no' is not supported in Red Hat Enterprise Linux and may cause several
UsePAM yes

[root@localhost Linux-PAM-1.1.8]# ldd /usr/sbin/sshd | head
	linux-vdso.so.1 =>  (0x00007ffc63d54000)
	libfipscheck.so.1 => /lib64/libfipscheck.so.1 (0x00007fa4628fe000)
	libwrap.so.0 => /lib64/libwrap.so.0 (0x00007fa4626f3000)
	libaudit.so.1 => /lib64/libaudit.so.1 (0x00007fa4624ca000)
	libpam.so.0 => /lib64/libpam.so.0 (0x00007fa4622bb000)          <-- 
	libselinux.so.1 => /lib64/libselinux.so.1 (0x00007fa462094000)
	libsystemd.so.0 => /lib64/libsystemd.so.0 (0x00007fa461e63000)
	libcrypto.so.10 => /lib64/libcrypto.so.10 (0x00007fa461a00000)
	libdl.so.2 => /lib64/libdl.so.2 (0x00007fa4617fc000)
	libldap-2.4.so.2 => /lib64/libldap-2.4.so.2 (0x00007fa4615a7000)


entOS 7.9.2009에서 사용법: 
# cd
# yum -y install yum-utils bzip2 flex-devel
# useradd mockbuild
# yumdownloader --downloadonly --source pam
# rpm -Uvh pam-1.1.8-23.el7.src.rpm

컴파일 에러 때문에 flex-devel을 설치했는데 그래도 
에러가 발생되서 개발툴 전체를 설치한다.
# yum -y groupinstall "Development Tools"

# cd rpmbuild/SOURCES
# tar xjf Linux-PAM-1.1.8.tar.bz2
# cd Linux-PAM-1.1.8
# ./configure
# make  <-- 첫 번째 make
  <== 컴파일이 완료되면 실행파일들이 생긴다.

[root@localhost Linux-PAM-1.1.8]# cd libpam
[root@localhost libpam]# vi pam_auth.c 

 13 
 14 int pam_authenticate(pam_handle_t *pamh, int flags)
 15 {
 16     int retval;
 17     printf(">>> %s: pam_authenticate() 호출! <<<\n", __FILE__);  //  __FILE__: pam_auth.c
 18 
 19     D(("pam_authenticate called"));
 20 

make 이후에 pam_auth.c 소스가 수정 되었기 때문에 다시 make로 컴파일을 해야 한다.
[root@localhost libpam]# make

각 오브젝트들을 공유라이브러리 libpam.so.0.83.1 로 묶어서 생성한다. 
[root@localhost libpam]# ls -Z .libs/
lrwxrwxrwx. root      root      unconfined_u:object_r:admin_home_t:s0 libpam.la -> ../libpam.la
-rw-r--r--. root      root      unconfined_u:object_r:admin_home_t:s0 libpam.lai
lrwxrwxrwx. root      root      unconfined_u:object_r:admin_home_t:s0 libpam.so -> libpam.so.0.83.1
lrwxrwxrwx. root      root      unconfined_u:object_r:admin_home_t:s0 libpam.so.0 -> libpam.so.0.83.1
-rwxr-xr-x. root      root      unconfined_u:object_r:admin_home_t:s0 libpam.so.0.83.1
-rw-r--r--. root      root      unconfined_u:object_r:admin_home_t:s0 pam_account.o
-rw-r--r--. root      root      unconfined_u:object_r:admin_home_t:s0 pam_audit.o
-rw-r--r--. root      root      unconfined_u:object_r:admin_home_t:s0 pam_auth.o
  :
  :(생략)

수정한 pam 공유라이브러리
[root@localhost libpam]# ls -l .libs/libpam.so*
lrwxrwxrwx. 1 root root     16  3월 27 23:48 .libs/libpam.so -> libpam.so.0.83.1
lrwxrwxrwx. 1 root root     16  3월 27 23:48 .libs/libpam.so.0 -> libpam.so.0.83.1
-rwxr-xr-x. 1 root root 234440  3월 27 23:48 .libs/libpam.so.0.83.1

원래의 pam 공유라이브러리
[root@localhost libpam]# ls -l /usr/lib64/libpam.so*
lrwxrwxrwx. 1 root root    16  3월  4 08:12 /usr/lib64/libpam.so -> libpam.so.0.83.1
lrwxrwxrwx. 1 root root    16  3월 27 01:46 /usr/lib64/libpam.so.0 -> libpam.so.0.83.1
-rwxr-xr-x. 1 root root 61680  4월  1  2020 /usr/lib64/libpam.so.0.83.1

컴파일된 공유라이브러리 libpam.so.0.83.1 파일을 /usr/lib64 디렉터리로 이동시킨다.
[root@localhost libpam]# mv .libs/libpam.so.0.83.1 /usr/lib64/
mv: overwrite `/usr/lib64/libpam.so.0.83.1'? y  <-- 이전 pam 공유라이브러리를 덮어쓴다.

[root@localhost libpam]# chcon -t lib_t /usr/lib64/libpam.so.0.83.1 

[root@localhost libpam]# su -
>>>pam_auth.c: pam_authenticate() 호출! <<<
마지막 로그인: 화  3월 28 00:09:52 KST 2023 일시 pts/2
[root@localhost ~]# 

PAM은 공유라이브러리, 동적라이브러리를 동시에 사용한다.
일반 애플리케이션(myprogram)이 PAM을 이용해서 인증을 하기 위해서는 PAM에 대한 소스코드를 작성한다.
컴파일할 때 공유/동적라이브러리로 컴파일한다.
# ldd myprogram
  :
	libpam.so.0 => /lib64/libpam.so.0 (0x00007fa4622bb000)          <-- 
  :
myprogram의 PAM 설정파일(/etc/pam.d/myprogram)에 설정을 한다.
PAM모듈을 사용하는데 이것은 동적라이브러리이다.


o Type

auth
사용자 인증에 사용

account
인증과 상관 없는 계정 관리를 수행. 사용자의 특성(위치, 시간, 권한) 등을 확인하여 접근 확인

password
패스워드를 변경 시 auth 모듈과 함께 사용 됨(패스워드 변경할 수 있는 모듈을 지정)

session
사용자 인증 수행 전 후에 수행(사용자 홈 디렉토리 마운팅/언마운팅, 로그인/로그아웃, 사용자 서비스 제공/제한, 로그 기록 등)

o Control
sufficient
모듈의 인증 반환이 성공인 경우 즉시 인증 성공
required의 실패 이 후에 호출된 경우나 모듈의 실패는 인증에 영향을 주지 않음

required
모듈의 인증 반환이 실패인 경우 다른 모듈의 인증 요청은 수행하나 최종적으로 인증은 거부(모듈의 성공 시 다음 모듈 호출)

requisite
모듈의 인증 반환이 실패인 경우 즉시 인증 거부(모듈의 성공 시 다음 모듈 호출)

optional
선택 사항. 보통 이 모듈의 인증 반환은 무시되나 인증의 성공/실패가 불분명 시 모듈의 결과에 따라 결정


실습> sufficient

sufficient
모듈의 인증 반환이 성공인 경우 즉시 인증 성공
required의 실패 이 후에 호출된 경우나 모듈의 실패는 인증에 영향을 주지 않음


터미널을 2개 열어서 작업한다.

첫 번째 터미널에서는 /etc/pam.d/su 파일을 열어서 pam_permit.so 를 추가한다.
# vi /etc/pam.d/su
  1 #%PAM-1.0
  2 auth        sufficient  pam_permit.so 
  3 auth        sufficient  pam_rootok.so
  :
  :(생략)

두 번째 터미널에서 pam을 다시 설치하고 user1로 변경한다.
# yum -y reinstall pam
# su - user1
[user1@localhost ~]$ su -
마지막 로그인: 화  3월 28 01:00:31 KST 2023 일시 pts/2
[root@localhost ~]# exit

pam_permit.so 의 순서를 변경한다.
# vi /etc/pam.d/su
  1 #%PAM-1.0
  2 auth        sufficient  pam_rootok.so 
  3 auth        sufficient  pam_permit.so


[user1@localhost ~]$ su -
마지막 로그인: 화  3월 28 01:04:29 KST 2023 일시 pts/2
[root@localhost ~]# 

실습> required

required
모듈의 인증 반환이 실패인 경우 다른 모듈의 인증 요청은 수행하나 최종적으로 인증은 거부(모듈의 성공 시 다음 모듈 호출)

-- /etc/pam.d/su --
  1 #%PAM-1.0
  2 auth        required    pam_rootok.so
  3 auth        sufficient  pam_permit.so
      :
      :(생략)
  8 auth        substack    system-auth debug
      :
      :(생략)
-- /etc/pam.d/su --

2번 라인의 auth        required    pam_rootok.so 이 부분을 수행하지만 실패이고 다음 모듈을 검사한다. 
3번 라인의 auth        sufficient  pam_permit.so 이 부분을 수행하지만 
위에서 required 에서 이미 실패했기 때문에 인증에 영향을 주지 않으므로 실패가 되고 다음 모듈을 검사한다.
8번 라인의 auth        substack    system-auth debug 에서도 root의 비밀번호를 정확하게 입력해도
2번 라인의 required 에서 인증 반환이 실패 되었기 때문에 최종적으로 인증은 실패가 되므로 로그인을 못한다.
[user1@localhost ~]$ su -
암호:
su: 인증 실패
[user1@localhost ~]$ 

실습> sufficient

sufficient
모듈의 인증 반환이 성공인 경우 즉시 인증 성공
required의 실패 이 후에 호출된 경우나 모듈의 실패는 인증에 영향을 주지 않음

-- /etc/pam.d/su --
  1 #%PAM-1.0
  2 auth        sufficient  pam_rootok.so
  3 auth        sufficient  pam_permit.so
     :
     : (생략)
-- /etc/pam.d/su --

2번 라인의 auth        sufficient  pam_rootok.so 이 부분을 수행하지만 실패이고 다음 모듈을 검사한다. 
3번 라인의 auth        sufficient  pam_permit.so 이 부분을 수행한다. 
위에서 sufficient 에서 실패했지만  때문에 인증에 영향을 주지 않으므로 즉시 인증이 성공이 되고 다음 모듈을 검사한다.
최종적으로 3번라인의 성공으로 로그인 된다. 

[user1@localhost ~]$ su -
마지막 로그인: 화  3월 28 01:30:33 KST 2023 일시 pts/2
[root@localhost ~]# 

실습> requisite

requisite
모듈의 인증 반환이 실패인 경우 즉시 인증 거부(모듈의 성공 시 다음 모듈 호출)


-- /etc/pam.d/su --
  1 #%PAM-1.0
  2 auth        requisite   pam_rootok.so
  3 auth        sufficient  pam_permit.so
      :
      :(생략)
  8 auth        substack    system-auth debug
      :
      :(생략)
-- /etc/pam.d/su --

2번 라인의 auth        requisite  pam_rootok.so 이 부분을 수행하지만 실패이므로 즉시 실패가 된다. 
모듈 검사는 종료 검사한다. 
3번 라인의 auth        sufficient  pam_permit.so 이 부분을 수행하지 않는다. 
[user1@localhost ~]$ su -
su: 인증 실패
[user1@localhost ~]$


o 소스 레벨로 접근
[root@localhost ~]# cd rpmbuild/SOURCES/Linux-PAM-1.1.8/modules/pam_permit
[root@localhost pam_permit]# pwd
/root/rpmbuild/SOURCES/Linux-PAM-1.1.8/modules/pam_permit

[root@localhost pam_permit]# vi pam_permit.c 
 33 PAM_EXTERN int
 34 pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED,
 35             int argc UNUSED, const char **argv UNUSED)
 36 {           
 37     int retval;
 38     const char *user=NULL;
 39     
 40     printf(">>> %s : pam_sm_authenticate() 호출! <<< \n", __FILE__);  // 이 부분을 추가한다.
 41     
 42     /*
 43      * authentication requires we know who the user wants to be
 44      */
 45     retval = pam_get_user(pamh, &user, NULL);

[root@localhost pam_permit]# make
/bin/sh ../../libtool --tag=CC   --mode=compile gcc -DHAVE_CONFIG_H -I. -I../..    -I../../libpam/include -I../../libpamc/include -g -O2 -MT pam_permit.lo -MD -MP -MF .deps/pam_permit.Tpo -c -o pam_permit.lo pam_permit.c
libtool: compile:  gcc -DHAVE_CONFIG_H -I. -I../.. -I../../libpam/include -I../../libpamc/include -g -O2 -MT pam_permit.lo -MD -MP -MF .deps/pam_permit.Tpo -c pam_permit.c  -fPIC -DPIC -o .libs/pam_permit.o
mv -f .deps/pam_permit.Tpo .deps/pam_permit.Plo
/bin/sh ../../libtool --tag=CC   --mode=link gcc -I../../libpam/include -I../../libpamc/include -g -O2 -no-undefined -avoid-version -module -Wl,--version-script=./../modules.map -Wl,--as-needed -Wl,--no-undefined -Wl,-O1 -o pam_permit.la -rpath /lib64/security pam_permit.lo ../../libpam/libpam.la 
libtool: link: rm -fr  .libs/pam_permit.la .libs/pam_permit.lai
libtool: link: gcc -shared  .libs/pam_permit.o   -Wl,-rpath -Wl,/root/rpmbuild/SOURCES/Linux-PAM-1.1.8/libpam/.libs -Wl,-rpath -Wl,/lib64 ../../libpam/.libs/libpam.so -ldl  -Wl,--version-script=./../modules.map -Wl,--as-needed -Wl,--no-undefined -Wl,-O1   -Wl,-soname -Wl,pam_permit.so -o .libs/pam_permit.so
libtool: link: ( cd ".libs" && rm -f "pam_permit.la" && ln -s "../pam_permit.la" "pam_permit.la" )

컴파일된 라이브러리 파일을 /etc/lib64/security 디렉터리에 복사한다.
[root@localhost pam_permit]# cp .libs/pam_permit.so /usr/lib64/security/
cp: overwrite `/usr/lib64/security/pam_permit.so'? y

SELinux를 잠시 해제한다.
[root@localhost pam_permit]# setenforce 0


-- /etc/pam.d/su --
  1 #%PAM-1.0
  2 auth        requisite   pam_rootok.so
  3 auth        sufficient  pam_permit.so
      :
      :(생략)
  8 auth        substack    system-auth debug
      :
      :(생략)
-- /etc/pam.d/su --

2번 라인의 auth        requisite  pam_rootok.so 이 부분을 수행하고 root가 맞으므로 성공이므로 다음 모듈을 검사한다.
requisite 로 설정되면 모듈의 성공 시 다음 모듈 호출하게 되어 있다.
3번 라인의 auth        sufficient  pam_permit.so 이 부분을 수행해서 성공한다. 
[root@localhost pam_permit]# su - user1
>>> pam_permit.c : pam_sm_authenticate() 호출! <<<
마지막 로그인: 화  3월 28 01:58:19 KST 2023 일시 pts/2
[user1@localhost ~]$ 

2번 라인의 auth        requisite  pam_rootok.so 이 부분을 수행하고 root가 아니므로 실패이므로 인증은 즉시 실패가 된다.
그리고 다음 모듈의 검사는 하지 않는다. 만약 pam_permit.so가 실행되었다면 아래 메세지가 출력이 되어야 하는데 출력되지 
않았다라는 것은 pam_permit.so 모듈이 호출되지 않았다는걸 의미한다.
>>> pam_permit.c : pam_sm_authenticate() 호출! <<<
[user1@localhost ~]$ su -
su: 인증 실패
[user1@localhost ~]$ 

실습> ssh로 접근할 때 무조건 로그인 시키기

# vi /etc/ssh/sshd_config
UsePAM yes

# vi /etc/pam.d/sshd
  1 #%PAM-1.0
  2 auth       sufficient   pam_permit.so
  3 auth       required     pam_sepermit.so
  4 auth       substack     password-auth
     :
     :(생략)
무조건 접속할 때 성공
auth       sufficient   pam_permit.so: 인증에 대해서 무조건 성공을 반환한다.

ssh로 접속할 때 비밀번호를 아무거나 넣어도 pam_permit.so에 의해서 성공을 반환하므로 로그인이 된다.
C:\Users\boani>ssh root@200.200.200.3
root@200.200.200.3's password:   <-- 비밀번호를 아무거나 넣는다.
Last login: Tue Mar 28 03:29:27 2023 from localhost
[root@localhost ~]# exit

실습> 자동화할 수 있는 스크립트 작성하기

[root@localhost pam_permit]# cd
[root@localhost ~]# mkdir bin
[root@localhost ~]# cat >> bin/PAMCompile.sh << EOF
#!/bin/sh
# 파일명: pamCompile.sh
# 프로그램 설명: PAM 분석을 위한 라이브러리 컴파일및 복사
# 작성자: 리눅스마스터넷

libdir=$(pwd)
#libname=${libdir##*/}
libname=`basename $libdir`
cd $libdir
pwd
make
cp -f .libs/${libname}.so /lib64/security/
EOF

[root@localhost ~]# chmod 755 bin/PAMCompile.sh

실습> pam_rootok 모듈 분석하기

1. 소스코드 변경
소스코드에 printf() 함수를 이용해서 출력하는 코드를 삽입한다.
[root@localhost ~]# cd /root/rpmbuild/SOURCES/Linux-PAM-1.1.8/modules/pam_rootok
[root@localhost pam_rootok]# vi pam_rootok.c 

 43 static int
 44 _pam_parse (const pam_handle_t *pamh, int argc, const char **argv)
 45 {
 46     int ctrl=0;
 47 
 48     printf("%s: _pam_parse() 실행!\n", __FILE__);
 49 
 50     /* step through arguments */
 51     for (ctrl=0; argc-- > 0; ++argv) {
 52 
 53         printf("for문 \n");
 54 
 55         /* generic options */
 56         // 인수가 디버그와 같으면 
 57         if (!strcmp(*argv,"debug"))
 58         {
 59             ctrl |= PAM_DEBUG_ARG;  // 1  // ctrl = ctrl | PAM_DEBUG_ARG;
 60             printf("debug 와 같음.\n");
 61         }
 62         // 인수가 디버그와 같지 않으면 
 63         else {
 64             printf("debug 와 같지 않음.\n");
 65 
 66             // /var/log/secure 파일에 로그를 기록함.
 67             // /usr/include/sys/syslog.h
 68             // #define  LOG_ERR     3   /* error conditions */
 69             pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
 70         }
 71     }
 72 
 73     printf("ctrl: %d\n", ctrl);
 74 
 75     return ctrl;
 76 }

2. PAMCompile.sh 실행
자동화된 스크립트를 실행하면 소스코드를 컴파일하고 동적 라이브러리를 복사한다.
저장 후 PAMCompile.sh 파일을 실행한다. P<tab> 을 이용해서 실행한다.
[root@localhost pam_rootok]# PAMCompile.sh 
/root/rpmbuild/SOURCES/Linux-PAM-1.1.8/modules/pam_rootok
/bin/sh ../../libtool --tag=CC   --mode=compile gcc -DHAVE_CONFIG_H -I. -I../..    -I../../libpam/include -I../../libpamc/include  -g -O2 -MT pam_rootok.lo -MD -MP -MF .deps/pam_rootok.Tpo -c -o pam_rootok.lo pam_rootok.c
libtool: compile:  gcc -DHAVE_CONFIG_H -I. -I../.. -I../../libpam/include -I../../libpamc/include -g -O2 -MT pam_rootok.lo -MD -MP -MF .deps/pam_rootok.Tpo -c pam_rootok.c  -fPIC -DPIC -o .libs/pam_rootok.o
mv -f .deps/pam_rootok.Tpo .deps/pam_rootok.Plo
/bin/sh ../../libtool --tag=CC   --mode=link gcc -I../../libpam/include -I../../libpamc/include  -g -O2 -no-undefined -avoid-version -module -Wl,--version-script=./../modules.map -Wl,--as-needed -Wl,--no-undefined -Wl,-O1 -o pam_rootok.la -rpath /lib64/security pam_rootok.lo ../../libpam/libpam.la   
libtool: link: rm -fr  .libs/pam_rootok.la .libs/pam_rootok.lai .libs/pam_rootok.so
libtool: link: gcc -shared  .libs/pam_rootok.o   -Wl,-rpath -Wl,/root/rpmbuild/SOURCES/Linux-PAM-1.1.8/libpam/.libs -Wl,-rpath -Wl,/lib64 ../../libpam/.libs/libpam.so -ldl  -Wl,--version-script=./../modules.map -Wl,--as-needed -Wl,--no-undefined -Wl,-O1   -Wl,-soname -Wl,pam_rootok.so -o .libs/pam_rootok.so
libtool: link: ( cd ".libs" && rm -f "pam_rootok.la" && ln -s "../pam_rootok.la" "pam_rootok.la" )

3. 인수가 없는 경우
터미널 3개로 작업한다. 
- 첫 번째 터미널: su -
- 두 번째 터미널: /etc/pam.d/su 수정
- 세 번째 터미널: 로그 모니터링

새로운 터미널을 열어서 로그를 모니터링한다.
[root@localhost ~]# > /var/log/secure
[root@localhost ~]# tail -f /var/log/secure

새로운 터미널을 열어서 /etc/pam.d/su 파일을 수정한다.
[root@localhost ~]# vi /etc/pam.d/su
  1 #%PAM-1.0
  2 auth        sufficient   pam_rootok.so 
  3 # Uncomment the following line to implicitly trust users in the "wheel" group.
  4 #auth       sufficient  pam_wheel.so trust use_uid debug
  5 # Uncomment the following line to require a user to be in the "wheel" group.
  6 #auth       required    pam_wheel.so use_uid

다른 터미널에서 su - 를 하면 인수가 없을 때는 for문 안으로 들어가지 않는다.
[root@localhost pam_rootok]# su - 
pam_rootok.c: _pam_parse() 실행!
ctrl: 0
[root@localhost ~]# 

로그는 아래처럼 기록된다.
[root@localhost ~]# tail -f /var/log/secure
Mar 28 04:57:18 localhost su: pam_succeed_if(su-l:account): 'uid' resolves to '0'
Mar 28 04:57:18 localhost su: pam_unix(su-l:session): session opened for user root by root(uid=0)
[root@localhost ~]# exit

4. 인수가 있는 경우
인수가 있고 정상적인 인수일 때 
터미널 3개로 작업한다. 
- 첫 번째 터미널: su -
- 두 번째 터미널: /etc/pam.d/su 수정
- 세 번째 터미널: 로그 모니터링

새로운 터미널에서 /etc/pam.d/su 파일을 수정한다.
[root@localhost ~]# vi /etc/pam.d/su
  1 #%PAM-1.0
  2 auth        sufficient  pam_rootok.so debug  <== debug가 추가된 경우
  3 # Uncomment the following line to implicitly trust users in the "wheel" group.
  4 #auth       sufficient  pam_wheel.so trust use_uid debug
  5 # Uncomment the following line to require a user to be in the "wheel" group.
  6 #auth       required    pam_wheel.so use_uid
  7 auth        substack    system-auth debug

로그를 초기화하고 모니터링 한다.
[root@localhost ~]# > /var/log/secure
[root@localhost ~]# tail -f /var/log/secure

su - 를 실행한다.
su - 를 할 때 인수가 있을 때는 for문 안으로 들어간다.
[root@localhost pam_rootok]# su - 
pam_rootok.c: _pam_parse() 실행!
for문 
debug 와 같음.
ctrl: 1
마지막 로그인: 화  3월 28 04:57:18 KST 2023 일시 pts/2
[root@localhost ~]# exit

debug 옵션을 주면 root check succeeded 로그가 기록된다.
[root@localhost ~]# > /var/log/secure
[root@localhost ~]# tail -f /var/log/secure
Mar 28 05:00:22 localhost su: pam_rootok(su-l:auth): root check succeeded  <-- debug 옵션에 의해서 저장된 로그
Mar 28 05:00:22 localhost su: pam_succeed_if(su-l:account): 'uid' resolves to '0'
Mar 28 05:00:22 localhost su: pam_unix(su-l:session): session opened for user root by root(uid=0)
Mar 28 05:00:53 localhost su: pam_unix(su-l:session): session closed for user root


5. 인수가 있는 경우
인수가 있지만 비정상적인(없는) 인수일 때 
터미널 3개로 작업한다. 
- 첫 번째 터미널: su -
- 두 번째 터미널: /etc/pam.d/su 수정
- 세 번째 터미널: 로그 모니터링

새로운 터미널에서 /etc/pam.d/su 파일을 수정한다.
[root@localhost ~]# vi /etc/pam.d/su
  1 #%PAM-1.0
  2 auth        sufficient  pam_rootok.so debug1  <== 잘못된 인수값
  3 # Uncomment the following line to implicitly trust users in the "wheel" group.
  4 #auth       sufficient  pam_wheel.so trust use_uid debug
  5 # Uncomment the following line to require a user to be in the "wheel" group.
  6 #auth       required    pam_wheel.so use_uid

로그를 모니터링한다.
[root@localhost ~]# > /var/log/secure
[root@localhost ~]# tail -f /var/log/secure

su - 명령어로 메세지를 확인한다.
[root@localhost pam_rootok]# su - 
pam_rootok.c: _pam_parse() 실행!
for문 
debug 와 같지 않음.
ctrl: 0
마지막 로그인: 화  3월 28 05:00:22 KST 2023 일시 pts/2
[root@localhost ~]# exit

로그에 기록된 내용은 아래처럼 기록된다.
[root@localhost ~]# tail -f /var/log/secure
Mar 28 05:09:45 localhost su: pam_rootok(su-l:auth): unknown option: debug1  <== 로그에 기록된 내용
Mar 28 05:09:45 localhost su: pam_unix(su-l:session): session opened for user root by root(uid=0)

실습> pam_wheel

1. 모듈 설명
모듈은 auth type에서만 사용 가능
모듈은 root로 접근하는 사용자가 wheel 그룹의 구성원인 경우에만 허용하기 위해 사용
일반적으로 su의 보안 설정을 위해 사용 됨
ex) su	auth	sufficient		pam_rootok.so
ex) su	auth	required		pam_wheel.so trust use_uid
ex) su	auth	required		pam_unix.so
ex) su	auth	required		pam_permit.so

개요
pam_wheel.so [ debug ] [ deny ] [ group=name ] [ root_only ] [ trust ] [ use_uid ]

설명
pam_wheel은 소위 wheel 그룹을 강제할 수 있는 PAM 모듈이다. 
따로 인수를 주지 않으면 요청 사용자가 wheel 그룹에 속한 경우에 대상 사용자 접근을 허가한다. 
그런 이름의 그룹이 없는 경우에는 그룹 ID가 0인 그룹을 쓴다.

옵션
debug
디버그 정보 찍기.

deny
인증 동작의 의미를 뒤집는다. 즉 UID 0 접근 권한을 얻으려고 하는 사용자가 wheel 그룹(내지 group 옵션 그룹)에 속해 있으면 접근을 거부한다. 반대로 사용자가 그 그룹에 없으면 PAM_IGNORE를 반환한다. (단 trust도 지정한 경우에는 PAM_SUCCESS를 반환한다.)

group=name
wheel 그룹 내지 GID 0 그룹 대신 name 그룹을 사용해 인증을 수행한다.

root_only
목표 사용자 UID가 0일 때만 wheel 소속 검사를 한다.

trust
사용자가 wheel 그룹에 속해 있으면 pam_wheel 모듈이 PAM_IGNORE 대신 PAM_SUCCESS를 반환한다. 
(그래서 모듈들을 잘 쌓으면 wheel 멤버가 패스워드 입력 없이 root로 su 하는 게 가능할 수도 있다.)

use_uid
사용 중인 터미널에 연계된 로그인 세션에서 사용자를 얻으려 하지 말고 대신 호출 프로세스의 실제 uid에 대해서 검사를 한다.

제공하는 모듈 종류
auth 및 account 모듈 타입을 제공한다.

반환 값
PAM_AUTH_ERR
인증 실패.

PAM_BUF_ERR
메모리 버퍼 오류.

PAM_IGNORE
PAM 처리부에서 반환 값을 무시해야 함.

PAM_PERM_DENY
권한 거부됨.

PAM_SERVICE_ERR
사용자 이름을 알아낼 수 없음.

PAM_SUCCESS
성공.

PAM_USER_UNKNOWN
알 수 없는 사용자.

예시
root 계정은 기본적으로 접근권을 얻고 (rootok), wheel 멤버만 root가 될 수 있되 (wheel) root 외 신청자들을 유닉스 방식으로 인증한다.

su      auth     sufficient     pam_rootok.so
su      auth     required       pam_wheel.so
su      auth     required       pam_unix.so

2. PAM 사용 예
-- /etc/pam.d/su --
  1 #%PAM-1.0
  2 auth        sufficient  pam_rootok.so debug
  3 # Uncomment the following line to implicitly trust users in the "wheel" group.
  4 auth        sufficient  pam_wheel.so trust use_uid debug
  5 # Uncomment the following line to require a user to be in the "wheel" group.
  6 #auth       required    pam_wheel.so use_uid
-- /etc/pam.d/su --

pam을 다시 원래대로 복구한다.
[root@localhost pam_rootok]# yum -y reinstall pam

사용자 2명을 생성하고 user1만 wheel그룹에 포함한다.
wheel 그룹이란 관리자 그룹을 말한다.
[root@localhost pam_rootok]# useradd user1
[root@localhost pam_rootok]# useradd user2
[root@localhost pam_rootok]# usermod -G wheel user1
[root@localhost pam_rootok]# su - user1
마지막 로그인: 화  3월 28 05:35:38 KST 2023 일시 pts/2
[user1@localhost ~]$  id
uid=1001(user1) gid=100(users) groups=100(users),10(wheel) context=unco ... (생략)

wheel 그룹에 포함되어 있으므로 trust 옵션과 use_uid 옵션이 존재하므로 비밀번호 인증이 없어도 바로 인증에 성공한다.
[user1@localhost ~]$ su -
마지막 로그인: 화  3월 28 05:09:45 KST 2023 일시 pts/2
[root@localhost ~]# exit
[user1@localhost ~]$ exit

3. 소스 레벨 분석
[root@localhost pam_rootok]# cd ../pam_wheel/
[root@localhost pam_wheel]# pwd
/root/rpmbuild/SOURCES/Linux-PAM-1.1.8/modules/pam_wheel
[root@localhost pam_wheel]# vi pam_wheel.c

 60 /* argument parsing */
 61 
 62 #define PAM_DEBUG_ARG       0x0001
 63 #define PAM_USE_UID_ARG     0x0002
 64 #define PAM_TRUST_ARG       0x0004
 65 #define PAM_DENY_ARG        0x0010
 66 #define PAM_ROOT_ONLY_ARG   0x0020
 67 
 68 static int
 69 _pam_parse (const pam_handle_t *pamh, int argc, const char **argv,
 70         char *use_group, size_t group_length)
 71 {
 72      int ctrl=0;
 73     
 74      printf("%s: _pam_parse() 호출! \n", __FILE__);   // 추가
 75      printf("argc: %d개\n", argc);  // 추가
 76      memset(use_group, '\0', group_length);
 77 
 78      /* step through arguments */
 79      for (ctrl=0; argc-- > 0; ++argv) {
 80 
 81           /* generic options */
 82 
 83           if (!strcmp(*argv,"debug"))
 84           { // 추가    
 85                ctrl |= PAM_DEBUG_ARG;
 86                printf("if debug: %d \n", ctrl);   // 추가
 87           } // 추가
 88           else if (!strcmp(*argv,"use_uid"))
 89           { // 추가    
 90                ctrl |= PAM_USE_UID_ARG;
 91                printf("else if use_uid: %d \n", ctrl);   // 추가
 92           } // 추가
 93           else if (!strcmp(*argv,"trust"))
 94           { // 추가
 95                ctrl |= PAM_TRUST_ARG;
 96                printf("else if trust: %d \n", ctrl);   // 추가
 97           } // 추가
 98           else if (!strcmp(*argv,"deny"))
 99           { // 추가
100                ctrl |= PAM_DENY_ARG;
101                printf("else if deny: %d \n", ctrl);   // 추가
102           } // 추가
103           else if (!strcmp(*argv,"root_only"))
104           { // 추가
105                ctrl |= PAM_ROOT_ONLY_ARG;
106                printf("else if root_only: %d \n", ctrl);   // 추가
107           } // 추가
108           else if (!strncmp(*argv,"group=",6))
109           { // 추가
110            strncpy(use_group,*argv+6,group_length-1);
111                printf("else if group=: %d \n", ctrl);  // 추가
112           } // 추가
113           else { // 추가
114                printf("else unknown option: %d \n", ctrl);  // 추가
115                pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv    );
116           } // 추가
117      }
118      printf("ctrl: %d\n", ctrl);  // 추가
119      return ctrl;

122 static int
123 perform_check (pam_handle_t *pamh, int ctrl, const char *use_group)
124 {
          :
          :(생략)

147     // PAM_ROOT_ONLY_ARG 옵션이 안들어 왔으므로 if안으로 들어가지 않는다.
148     // #define PAM_ROOT_ONLY_ARG   0x0020
149     //
150     //   00010000  <-- PAM_ROOT_ONLY_ARG (0x0020 = 32)
151     // & 00000111  <-- ctrl (7)
152     // ----------
153     //   00000000
154     if (ctrl & PAM_ROOT_ONLY_ARG) {  // <-- 연산 결과에 의해서 if(0) 거짓이 된다. 
155        printf("ctl & PAM_ROOT_ONLY_ARG\n");
156     /* su to a non uid 0 account ? */
157         if (pwd->pw_uid != 0) {
158             return PAM_IGNORE;
159         }
160     }
161 
162     // PAM_USE_UID_ARG 옵션이 들어 왔으므로 if안으로 들어간다. 
163     // #define PAM_USE_UID_ARG     0x0002
164     //   00000010  <-- PAM_USE_UID_ARG (0x0002 = 2)
165     // & 00000111  <-- ctrl (7)
166     // ----------
167     //   00000010
168     if (ctrl & PAM_USE_UID_ARG) {  // <-- 연산 결과에 의해서 if(2) 참이 된다.
169        printf("ctl & PAM_USE_UID_ARG\n");
170     tpwd = pam_modutil_getpwuid (pamh, getuid());
171     if (!tpwd) {
172         if (ctrl & PAM_DEBUG_ARG) {
173                 pam_syslog(pamh, LOG_NOTICE, "who is running me ?!");
174         }
175         return PAM_SERVICE_ERR;
176     }

        :
        :(생략)
269 
270 /* --- authentication management functions --- */
271 
272 PAM_EXTERN int
273 pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED,
274              int argc, const char **argv)
275 {
276 
277     char use_group[BUFSIZ];
278     int ctrl;
279 
280     printf("%s: pam_sm_authenticate() 호출! \n", __FILE__);
281 
282     ctrl = _pam_parse(pamh, argc, argv, use_group, sizeof(use_group));
283 
284     return perform_check(pamh, ctrl, use_group);
285 }

소스코드를 저장하고 셸로 나온 후 라이브러리를 다시 컴파일하고 복사한다.
[root@localhost pam_wheel]# PAMCompile.sh 

user1 사용자로 변경해서 실행한다.
[root@localhost pam_wheel]# su - user1
마지막 로그인: 화  3월 28 05:36:00 KST 2023 일시 pts/2

user1 사용자는 wheel 그룹에 포함되어 있다.
[user1@localhost ~]$ id
uid=1001(user1) gid=100(users) groups=100(users),10(wheel) ... (생략)

[user1@localhost ~]$ su -
pam_wheel.c: pam_sm_authenticate() 호출! 
pam_wheel.c: _pam_parse() 호출! 
argc: 3개
else if trust: 4 
else if use_uid: 6 
if debug: 7 
ctrl: 7
ctl & PAM_USE_UID_ARG  <-- use_uid 옵션이 들어왔으므로 이 부분이 출력된다.(나머지 옵션은 출력하지 않았음.)
마지막 로그인: 화  3월 28 06:38:49 KST 2023 일시 pts/2
[root@localhost ~]# exit
[user1@localhost ~]$ exit


실습> pam_module 구조체 확인하기

pam_module 구조체의 멤버 변수들은 모듈명과 함수 포인터로 구성되어 있다. 

-- /root/rpmbuild/SOURCES/Linux-PAM-1.1.8/libpam/include/security/pam_modules.h --

 37 struct pam_module {
 38     const char *name;       /* Name of the module */
 39 
 40     /* These are function pointers to the module's key functions.  */
 41 
 42     int (*pam_sm_authenticate) (pam_handle_t *pamh, int flags, int argc, const char **argv);  // auth
 43     int (*pam_sm_setcred)      (pam_handle_t *pamh, int flags, int argc, const char **argv);  // auth
 44     int (*pam_sm_acct_mgmt)    (pam_handle_t *pamh, int flags, int argc, const char **argv);  // account
 45     int (*pam_sm_open_session) (pam_handle_t *pamh, int flags, int argc, const char **argv);  // session
 46     int (*pam_sm_close_session)(pam_handle_t *pamh, int flags, int argc, const char **argv);  // session
 47     int (*pam_sm_chauthtok)    (pam_handle_t *pamh, int flags, int argc, const char **argv);  // password
 48 };

-- /root/rpmbuild/SOURCES/Linux-PAM-1.1.8/libpam/include/security/pam_modules.h --
 
o pam_succeed_if.so
모듈은 모든 타입에서 사용 가능
모듈의 인자로 조건을 설정하며, 조건이 참일 경우 성공을 반환
ex) auth	sufficient		user = user1 use_uid quiet

pam_succeed_if.c

578 /* static module data */
579 #ifdef PAM_STATIC
580 struct pam_module _pam_succeed_if_modstruct = {
581     "pam_succeed_if",       // name
582     pam_sm_authenticate,    // auth
583     pam_sm_setcred,         // auth
584     pam_sm_acct_mgmt,       // account
585     pam_sm_open_session,    // session
586     pam_sm_close_session,   // session
587     pam_sm_chauthtok        // password
588 };
589 #endif


o pam_wheel.so
모듈은 auth, account type에서만 사용 가능
모듈은 root로 접근하는 사용자가 wheel 그룹의 구성원인 경우에만 허용하기 위해 사용
일반적으로 su의 보안 설정을 위해 사용 됨
ex) su	auth	sufficient		pam_rootok.so
ex) su	auth	required		pam_wheel.so trust use_uid
ex) su	auth	required		pam_unix.so
ex) su	auth	required		pam_permit.so

pam_wheel.c 

266 #ifdef PAM_STATIC
267 
268 /* static module data */
269 
270 struct pam_module _pam_wheel_modstruct = {
271     "pam_wheel",            // name
272     pam_sm_authenticate,    // auth
273     pam_sm_setcred,         // auth
274     pam_sm_acct_mgmt,       // account
275     NULL,
276     NULL,
277     NULL
278 };
279 
280 #endif /* PAM_STATIC */


o pam_motd
모듈은 session type에서만 사용 가능
motd 파일의 내용을 출력
ex) session	optional	pam_motd.so	motd=/etc/motd

pam_motd.c

114 #ifdef PAM_STATIC
115      
116 /* static module data */
117 
118 struct pam_module _pam_motd_modstruct = {
119      "pam_motd",
120      NULL,
121      NULL,
122      NULL,
123      pam_sm_open_session,    // session
124      pam_sm_close_session,   // session
125      NULL,
126 };
127 
128 #endif
129 
130 /* end of module definition */


pam_time.so
모듈은 account type에서만 사용 가능
로그인할 위치 및 시간을 지정하여 시간에 따라 로그인을 제어
ex) account	required	pam_time.so
설정 파일 : /etc/security/time.conf

pam_time.c

667 #ifdef PAM_STATIC
668     
669 /* static module data */
670     
671 struct pam_module _pam_time_modstruct = {
672     "pam_time",
673     NULL,
674     NULL,
675     pam_sm_acct_mgmt,      // account
676     NULL,
677     NULL,
678     NULL
679 };  
680 #endif


pam_cracklib.so
모듈은 password 타입에서만 사용 가능
사용자 암호 정책을 확인
입력받은 암호를 /etc/lib64/cracklib_dict에 있는 사전 정보와 비교
새로운 암호를 /etc/security/opasswd에 저장되어 있는 이 전 암호 목록과 비교
시스템 보안 차원에서 패스워드 글자 수를 제한할 수 있음


pam_cracklib.c 
	
861 #ifdef PAM_STATIC
862 /* static module data */
863 struct pam_module _pam_cracklib_modstruct = {
864      "pam_cracklib",
865      NULL,
866      NULL,
867      NULL,
868      NULL,
869      NULL,
870      pam_sm_chauthtok     // password
871 };
872 #endif


pam_limits.so
모듈은 session type에서만 사용 가능
사용자가 사용할 수 있는 로그인 세션의 자원을 설정 파일에 따라 제한
사용자에 가용 자원을 제한함으로써 자원에 영향을 주는 DoS 공격에 대비가 가능하다는 이점이 있음
ex) session	required	pam_limits.so
설정 파일 : /etc/security/limits.conf

pam_limits.c  

1095 #ifdef PAM_STATIC
1096 
1097 /* static module data */
1098 
1099 struct pam_module _pam_limits_modstruct = {
1100      "pam_limits",
1101      NULL,
1102      NULL,
1103      NULL,
1104      pam_sm_open_session,     // session
1105      pam_sm_close_session,    // session
1106      NULL
1107 };
1108 #endif
profile
정보보안 전문가

0개의 댓글