사람마다 푸는 방식이 다를 수 있으니. 어려움에 있으실 때 참조만 하시고, 직접 풀어보시는 것 을 추천합니다.
level10 의 시작.
구동 환경 : Windows 7
서버 환경 : VMware WorkStation 10.0 Redhat9.0 hackerschool FTZ
사용 플램 : putty (퓨티)
login as : level10
password :
level9에서 끝냈을 때 받았던 비밀번호를 입력 해준 다음에 로그인 하면 된다.
그리고 여태까지 늘 힌트를 찾았던 ls -al 명령어를 실행
[level10@ftz level10]$ ls -al
total 84
drwxr-xr-x 5 root level10 4096 Jan 14 2010 .
drwxr-xr-x 34 root root 4096 Sep 30 02:00 ..
-rw------- 1 root root 1 Jan 15 2010 .bash_history
-rw-r--r-- 1 root root 24 Feb 24 2002 .bash_logout
-rw-r--r-- 1 root root 224 Feb 24 2002 .bash_profile
-rw-r--r-- 1 root root 151 Feb 24 2002 .bashrc
-rw-r--r-- 1 root root 400 Sep 24 2000 .cshrc
-rw-r--r-- 1 root root 4742 Sep 24 2000 .emacs
-r--r--r-- 1 root root 319 Sep 24 2000 .gtkrc
-rw-r--r-- 1 root root 100 Sep 24 2000 .gvimrc
-rw-r----- 1 root level10 253 Jan 14 2010 hint
-rw-r--r-- 1 root root 226 Sep 24 2000 .muttrc
-rw-r--r-- 1 root root 367 Sep 24 2000 .profile
drwxr-x--- 2 root root 4096 Mar 29 2003 program
drwxr-xr-x 2 root level10 4096 Feb 24 2002 public_html
drwxrwxr-x 2 root level10 4096 Jan 16 2009 tmp
-rw------- 1 root root 1 May 7 2002 .viminfo
-rw-r--r-- 1 root root 4145 Sep 24 2000 .vimrc
-rw-r--r-- 1 root root 245 Sep 24 2000 .Xdefaults
힌트는 위와 같이 나왔다.
공유 메모리에 대한 문제이다.
처음에는 이 문제를 접했을 때, 공유 메모리에 대해서 검색했다.
그랬더니 공유 메모리에 대한 정리가 깔끔하게 적힌 사이트가 나와 있어서 아래에 내용을 가져왔다.
공유메모리 (shared memory) ?
출처 ( http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/system_programing/IPC/SharedMemory )
보통 프로세스에서 사용되는 메모리영역은 해당 프로세스만이사용할수 있다.
하지만 때때로 여러개의 프로세스가 특정 메모리영역을 사용했으면 하는때가 있을것이다.
System V IPC 설비중의 하나인 "공유메모리"를 통해서 이러한일을 할수있다.
#개요
모든 프로세스(:12)는 자신의 업무를 수행하기 위해서 필요한 자료를 저장하기 위한 메모리 공간을 가지게 된다.
이러한 메모리공간에는 CPU에 의해 수행되는 명령어들, 프로그램 시작시 정의되고 초기화된 데이타, 프로그램 시작시 정의되었지만 초기화 되지 않은 데이타, 함수호출에 필요한 정보, 동적할당이 이루어지는 데이타등 이 들어가게 된다.
hjh 프로세스는 시작시 혹은 실행중에 이러한 데이타를 저장하고 사용하기 위한 메모리 공간을 커널에 요구하여서 할당받아 사용하게 되는데, 이러한 메모리공간은 기본적으로 메모리를 요청한 프로세스만이 접근가능하도록 되어있다.
하지만 가끔은 여러개의 프로세스가 특정 메모리 공간을 동시에 접근해야할 필요성을 가질때가 있을것이다.
공유메모리는 이러한 작업을 위한 효율적인 방법을 제공한다.
공유메모리는 여러 IPC 중에서 가장 빠른 수행속도를 보여준다.
그이유는 하나의 메모리를 공유해서 접근하게 되므로, 데이타 복사와 같은 불필요한 오버헤드가 발생하지 않기 때문으로, 빠른 데이타의 이용이 가능하다. 그러나 하나의 프로세스가 메모리에 접근중에 있을때, 또다른 프로세스가 메모리에 접근하는 일이 발생하면 자칫 데이타가 홰손될수 있을것이므로, 한번에 하나의 프로세스가 메모리에 접근하고 있다는걸 보증해줄수 있어야 할것이다.
이러한 작업을 위해서 Unix(:12) 에서는 Semaphore(:12) 라는 또다른 공유자원을 제어할수 있도록 해주는 도구를 제공해준다. 이번 문서에서는 Semaphore 를 다루지는 않을것이다. 이것은 다른 문서에서 다루도록 하고 여기에서는 단지 공유메모리에 대해서만 다루도록 할것이다.
다음은 공유메모리에 관련된 함수들의 모음이다.
#include <sys/types.h>
#include <sys/shm.h>
int shmget(key_t key, int size, int shmflg)
void *shmat( int shmid, const void *shmaddr, int shmflg )
int shmdt( const void *shmaddr)
int shmctl(int shmid, int cmd, struct shmid_ds *buf)
#공유메모리는 어떻게 할당되는가
위의 함수들을 설명하기 전에 우선 공유메모리가 어떻게 할당되고, 어떤 과정을 통해서 접근가능한지에 대해서 우선 알아보도록 하겠다.
공유메모리의 생성요청은 최초 공유메모리 영역을 만드는 프로세스가 커널에 공유메모리 공간의 할당을 요청함으로써 이루어지며, 만들어진 공유메모리는 커널에 의해서 관리 되게 된다.
이런 이유로 한번만들어진 공유메모리는 운영체제를 리부팅하거나, 직접 공유메모리 공간을 삭제시켜주지 않은한, 공유메모리를 사용하는 모든 프로세스가 없어졌다고 하더라도, 계속적으로 유지되게 된다.
프로세스가 커널에게 공유메모리 공간을 요청하게 되면, 커널은 공유메모리 공간을 할당시켜주고 이들 공유메모리공간을 관리하기 위한 내부자료구조를 통하여, 이들 공유메모리를 관리하게 된다. 이 자료는 shmid_ds 라는 구조체에 의해서 관리되며 shm.h 에 정의되어 있다.
struct shmid_ds
{
struct ipc_perm shm_perm; // 퍼미션
int shm_segsz; // 메모리 공간의 크기
time_t shm_dtime; // 마지막 attach 시간
time_t shm_dtime; // 마지막 detach 시간
time_t shm_ctime; // 마지막 변경 시간
unsigned short shm_cpid; // 생성프로세스의 pid
unsigned short shm_lpid; // 마지막으로 작동한 프로세스의 pid
short shm_nattch; // 현재 접근한 프로세스의 수
};
Unix 버젼에 따라서 멤버변수들이 약간씩 차이를 보일수 있다.
shm_perm 공유메모리는 여러개의 프로세스가 동시에 접근 가능하므로, 파일과 같이 그 접근권한을 분명히 명시해줘야 한다.
shm_segsz 할당된 메모리의 byte 크기이다
shm_atime 가장최근의 프로세스가 세그먼트를 attach한 시간
shm_dtime 가장최근의 프로세스가 세그먼트를 detach한 시간
shm_ctime 마지막으로 이 구조체가 변경된 시간
shm_cpid 이 구조체를 생성한 프로세스의 pid
shm_lpid 마지막으로 작동을 수행한 프로세스의 pid
shm_nattch 현재 접근중인 프로세스의 수
이러한 공유메모리에 접근을 하기 위해서는 고유의 공유메모리 key 를 통해서 접근가능해지며, 이 key값을 통해서 다른 여러개의 공유메모리들과 구분되어 질수 있다.
#shmget
shmget 은 커널에 공유메모리 공간을 요청하기 위해 호출하는 시스템 호출 함수이다. key 는 바로 위에서 설명했듯이 고유의 공유메모리임을 알려주기 위해서 사용된다. shmget 을 이용해서 새로운 공유메모리 영역을 생성하거나 기존에 만들어져있던 공유메모리 영역을 참조할수 있다
첫번째 아규먼트는 여러개의 공유메모리중 원하는 공유메모리에 접근하기 위한 Key 값이다. 이 Key 값은 커널에 의해서 관리되며, Key 값을 통해서 선택적인 공유메모리에의 접근이 가능하다. 두번째 아규먼트는 공유메모리 의 최소크기 이다. 새로운 공유메모리를 생성하고자 한다면 크기를 명시해주어야 한다. 존재하는 메모리를 참조한다면 크기는 0으로 명시한다. 3번째 아규먼트는 공유메모리의 접근권한과, 생성방식을 명시하기 위해서 사용한다. 아규먼트의 생성방식을 지정하기 위해서 IPC_CREAT 와 IPC_EXCL 을 사용할수 있다. 아래 이들에 대해서 설명을 해두었다.
IPC_CREAT
key 를 이용 새로운 공유메모리 공간을 만든다.
IPC_EXCL
IPC_CREAT와 같이 사용되며, 공유메모리 공간이 이미 존재할경우 error 를 되돌려준다.
만약 IPC_CREAT 만 사용된다면 shmget()은 새로 생성되는 공유메모리공간을 지시하는 공유메모리공간 "식별자" 되돌려준다. 만약 입력된 key 값이 지시하는 공유메모리 공간이 이미 존재하고 있다면 존재하는 공유메모리 공간의 "식별자"를 되돌려준다. IPC_EXCL 과 IPC_CREAT 를 같이 사용할경우, 공유메모리 공간이 존재하지 않으면 새로 생성시켜주며, 존재할경우에 error 를 되돌려준다.
3번째 아규먼트는 이외에도 권한을 지정해줄수도 있다. 권한은 파일권한과 동일하게, 유저, 그룹, Other 에 대한 읽기/쓰기 권한을 지정할수 있다. 단 실행권한은 줄수 없도록 되어 있다. 아래와 같이 사용가능하다.
int shmid;
key_t keyval;
keyval = 1234;
shmid = shmget(keyval, 1024, IPC_CREAT | 0666));
if (shmid == -1)
{
return -1;
}
#shmat
일단 공유메모리 공간을 생성했으면, 우리는 공유메모리에 접근할수 있는 int 형의 "식별자" 를 얻게 된다. 우리는 이 식별자를 shmat 를 이용해서 지금의 프로세스가 공유메모리를 사용가능하도록 "덧붙임" 작업을 해주어야 한다.
첫번째 아규먼트는 shmget을 이용해서 얻어낸 식별자 번호이며, 두번째 아규먼트는 메모리가 붙을 주소를 명시하기 위해 사용하는데, 0을 사용할경우 커널이 메모리가 붙을 주소를 명시하게 된다. 특별한 사항이 없다면 0을 사용하도록 한다. 세번째 아규먼트를 이용해서, 우리는 해당 공유메모리에 대한 "읽기전용", "읽기/쓰기가능" 모드로 열수 있는데, SHM_RDONLY를 지정할경우 읽기 전용으로, 아무값도 지정하지 않을경우 "읽기/쓰기 가능" 모드로 열리게 된다.
#shmdt
프로세스가 더이상 공유메모리를 사용할필요가 없을경우 프로세스와 공유메모리를 분리 하기 위해서 사용한다. 이 함수를 호출할 경우 단지 현재 프로세스와 공유메모리를 분리시킬뿐이지, 공유메모리 내용을 삭제하지는 않는다는 점을 기억해야 한다. 공유메모리를 커널상에서 삭제 시키길 원한다면 shmctl 같은 함수를 이용해야 한다.
shmdt 가 성공적으로 수행되면 커널은 shmid_ds 의 내용을 갱신한다. 즉 shm_dtime, shm_lpid, shm_nattch 등의 내용을 갱신하는데, shm_dtime 는 가장 최근에 dettach (즉 shmdt 를 사용한)된 시간, shm_lpid 는 호출한 프로세세의 PID, shm_nattch 는 현재 공유메모리를 사용하는 (shmat 를 이용해서 공유메모리에 붙어있는) 프로세스의 수를 돌려준다. shmdt 를 사용하게 되면 shm_nattch 는 1 감소하게 될것이며, shm_nattch 가 0 즉 더이상 붙어있는 프로세스가 없다라는 뜻이 될것이다. shm_nattch 가 0이 되어있을때 만약 이 공유메모리가 shm_ctl 등에 의해 삭제표시 가 되어 있다면, 이 공유메모리는 삭제되게 된다.
#shmctl
이것은 공유메모리를 제어하기 위해서 사용한다. 즉 shmid_ds 를 직접 제어함으로써, 해당 공유메모리에 대한 소유자, 그룹 등의 허가권을 변경하거나, 공유메모리를 삭제혹은, 공유메모리의 잠금을 설정하거나 해제하는 등의 작업을 한다.
2번째 아규먼트를 이용해서 shmid 가 가르키는 공유메모리를 제어하며, cmd 를 이용해서 원하는 제어를 할수 있다. cmd 를 이용해 내릴수 있는 명령에는 다음과 같은 것들이 있다.
IPC_STAT 공유메모리 공간에 관한 정보를 가져오기 위해서 사용된다. 정보는 buf 에 저장된다.
IPC_SET 공유메모리 공간에 대한 사용자권한 변경을 위해서 사용된다. 사용자 권한 변경을 위해서는 슈퍼유저 혹은 사용자권한을 가지고 있어야 한다.
IPC_RMID 공유메모리 공간을 삭제하기 위해서 사용된다. 이 명령을 사용한다고 해서 곧바로 사용되는건 아니며, 더이상 공유메모리 공간을 사용하는 프로세스가 없을때, 즉 shm_nattch 가 0일때 까지 기다렸다가 삭제된다. 즉 해당 공유메모리 공간에 대해서 삭제표시를 하는거라고 생각하면 된다.
다음은 실제로 공유메모리를 사용하는 방법에 대한 가장간단한 예제이다. 자식과 부모프로세스간에 어떻게 메모리가 공유되는지 보여준다.
예제 : shm.c
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <unistd.h>
int main()
{
int shmid;
int pid;
int *cal_num;
void *shared_memory = (void *)0;
// 공유메모리 공간을 만든다.
shmid = shmget((key_t)1234, sizeof(int), 0666|IPC_CREAT);
if (shmid == -1)
{
perror("shmget failed : ");
exit(0);
}
// 공유메모리를 사용하기 위해 프로세스메모리에 붙인다.
shared_memory = shmat(shmid, (void *)0, 0);
if (shared_memory == (void *)-1)
{
perror("shmat failed : ");
exit(0);
}
cal_num = (int *)shared_memory;
pid = fork();
if (pid == 0)
{
shmid = shmget((key_t)1234, sizeof(int), 0);
if (shmid == -1)
{
perror("shmget failed : ");
exit(0);
}
shared_memory = shmat(shmid, (void *)0, 0666|IPC_CREAT);
if (shared_memory == (void *)-1)
{
perror("shmat failed : ");
exit(0);
}
cal_num = (int *)shared_memory;
*cal_num = 1;
while(1)
{
*cal_num = *cal_num + 1;
printf("child %d\n", *cal_num);
sleep(1);
}
}
// 부모 프로세스로 공유메모리의 내용을 보여준다.
else if(pid > 0)
{
while(1)
{
sleep(1);
printf("%d\n", *cal_num);
}
}
}
예제 프로그램이 하는 일은 간단하다.
int 형의 공유메모리 공간을 할당한다음. 자식프로세스에서 여기에 1씩을 더하고 부모프로세스에서는 공유메모리 내용을 출력하는 일을한다.
# 공유메모리 제어하기
쉘에서 공유메모리의 상황을 보여주기 위해서 ipcs(1)란 도구를 제공한다.
ipcs 를 사용하면 공유메모리 뿐만 아닌, Semaphore, Message Queue 등 소위 sytem V IPC 설비에 대한 내용을 보여준다.
그리고 ipcrm 도구를 이용해서 필요없는 공유메모리, Message Queue, Semaphore 등을 지워줄수 있다.
위의 예제코드를 컴파일 시켜서 실행시킨다음 ipcs 를 이용해서 확인을 해보면 공유메모리 자원이 어떤식으로 관리되는지 좀더 이해를 쉽게 할수 있을것이다.
# 공유 메모리 정보 확인
-l 옵션과 함께 ipcs를 실행하면 ipc(:12)자원 제한 정보를 확인할 수 있다.
$ ipcs -l
------ Shared Memory Limits --------
max number of segments = 4096
max seg size (kbytes) = 32768
max total shared memory (kbytes) = 8388608
min seg size (bytes) = 1
------ Semaphore Limits --------
max number of arrays = 128
max semaphores per array = 250
max semaphores system wide = 32000
max ops per semop call = 32
semaphore max value = 32767
------ Messages: Limits --------
max queues system wide = 1706
max size of message (bytes) = 8192
default max size of queue (bytes) = 16384
-m 옵션으로 실행하면 현재 사용중인 ipc 자원 정보를 확인할 수 있다.
$ ipcs -m
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 0 root 777 135168 2
0x00000000 819201 yundream 600 393216 2 dest
0x00000000 950274 yundream 600 393216 2 dest
0x00000000 983043 yundream 600 393216 2 dest
0x00000000 917508 yundream 600 393216 2 dest
0x00000000 1015813 yundream 600 393216 2 dest
0x00000000 1048582 yundream 600 393216 2 dest
0x00000000 27590663 yundream 600 393216 2 dest
0x00000000 35684360 yundream 666 4343780 2 dest
0x00000000 35717129 yundream 666 282808 2 dest
# /proc 파일 시스템으로 제어하기
리눅스 운영체제(:12)는 /proc 파일 시스템으로 공유 메모리 자원 값을 변경할 수 있다.
/proc/sys/kernel/shmmax : 프로세스가 생성할 수 있는 공유 메모리의 최대 크기
/proc/sys/kernel/shmall : 현재 사용중인 공유 메모리 크기
프로세스가 생성할 수 있는 공유 메모리 크기는 다음과 같이 변경하면 된다.
// 500M로
# echo 536870912 > /proc/sys/kernel/shmmax
공유 메모리에 대한 공부를 했다면 이제, 문제를 풀러 가보자.
[level10@ftz level10]$ cat hint
두명의 사용자가 대화방을 이용하여 비밀스런 대화를 나누고 있다.
그 대화방은 공유 메모리를 이용하여 만들어졌으며,
key_t의 값은 7530이다. 이를 이용해 두 사람의 대화를 도청하여
level11의 권한을 얻어라.
- 레벨을 완료하셨다면 소스는 지우고 나가주세요.
힌트를 다시 한번 봐보면, key_t 의 값은 7530 이라고 적혀있다.
근데 뭐가 뭐고 알 수가 없었기에, 공유 메모리 관련된 명령어인 ipcs 를 쳐서 내용을 보았다.
[level10@ftz level10]$ ipcs
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00001d6a 0 root 666 1028 0
0x46532e4f 32769 trainer1 777 5 0
------ Semaphore Arrays --------
key semid owner perms nsems
------ Message Queues --------
key msqid owner perms used-bytes messages
[level10@ftz level10]$
우리가 봐야할 것은 빨간색으로 적힌 4가지의 내용이다.
해석을 해보자면,
key : 0x00001d6a 16진수
를, 10진수로 변환부터 시켜보면,
[18:32:50] @진수변환 16 0x00001d6a
[18:32:50] <봇> [ 진수변환 ] ' 00001d6a ' 16진수(Hex) -> 10진수(Decimal) 변환 결과 : 7530
[위의 프로그램은 개인적으로 프로그래밍으로 만들어 둔 프로그램으로 변환 시킨 것이다.]
7530이라는 10진수의 값이 나온다.
힌트에서 알려줬던 것이 7530이라는 key_t 힌트이었다.
그리고 root 권한을 가진사림이 존재하고 퍼미션은 666 이라고 한다. byte의 크기는 1028byte 이다.
현재 디텍토리에서는 cat 명령어로 .c 파일을 만들 수 없으니, /tmp 임시파일 디텍토리로 이동하자.
그런다음 C 파일을 작성하자. ( 공유메모리를 공부한 상태이어야한다. )
[level10@ftz tmp]$ cat > abc.c
#include <stdio.h>
#include <sys/shm.h> // 공유메모리 관련 헤더파일
#include <sys/ipc.h> // 공유메모리 관련 헤더파일
int main(){
int i; // int 형으로 변수 i 선언
char *pA; // char 형으로 포인터변수 pA 선언
i = shmget(7530, 1028, IPC_CREAT|0666); // 변수 i 에 내용을 넣는다.
// shmget ?
// shmget([key_t key], [int size], [int shmflg]);
// key_t=7530, size=1028, shmflg 는 키값 공유메모리가 없어, IPC_CREAT 옵션으로 퍼미션 0666을 설정한다.
pA = shmat(i, NULL, 0); // shmat 를 이용해서 공유메모리를 "사용가능" 으로 변경
printf("%s", pA); // pA를 문자열로 출력한다.
return 0; // '0' 반환하여 main 을 끝낸다.
}
흰색이 아닌 색상이 들어간 내용들은 주석처리이다.
어려움을 느낄 수도 있어서 주석처리를 했다. 프로그램을 작성한 후, 이제 gcc로 컴파일을 하자.
[level10@ftz tmp]$ gcc -o abc abc.c
[level10@ftz tmp]$ ls -al
total 40
drwxrwxrwt 4 root root 4096 Sep 30 21:42 .
drwxr-xr-x 19 root root 4096 Sep 30 02:44 ..
-rwxrwxr-x 1 level10 level10 11767 Sep 30 21:42 abc
-rw-rw-r-- 1 level10 level10 193 Sep 30 21:41 abc.c
drwxrwxrwt 2 xfs xfs 4096 Sep 30 02:44 .font-unix
-rw-rw-r-- 1 level5 level5 31 Sep 30 13:49 level5.tmp
drwxr-xr-x 2 root root 4096 Sep 30 10:38 .mozilla
-rw------- 1 root root 1024 Sep 30 10:32 .rnd
gcc로 컴파일성공, 그리고 현재 있는 디텍토리의 내용을 확인.
abc 라는 파일이 생겨졌음을 알 수있다. 그렇다면 ./ 명령어로 abc 를 실행해보자.
[level10@ftz tmp]$ ./abc
멍멍: level11의 패스워드는?
구타: ~~~~~~~~~
패스워드가 출력이 되었다.
이제 레벨11로 이동하자. BOF 문제여서 풀이를 작성하기도 힘들 수 있겠다.. 노력하고 공부해서 풀도록 하겠다.
'정보보안 카테고리 > hackschool FTZ' 카테고리의 다른 글
hackerschool FTZ : level11 풀이 (0) | 2013.10.20 |
---|---|
hackerschool FTZ : level9 풀이 (0) | 2013.10.03 |
hackerschool FTZ : level8 풀이 (0) | 2013.10.02 |
hackerschool FTZ : level7 풀이 (0) | 2013.10.02 |
hackerschool FTZ : level6 풀이 (0) | 2013.10.01 |