Nmap을 이용한 네트워크 스캐닝과 방어하기(Scanning and Defending Networks with Nmap)

Nmap을 이용한 네트워크 스캐닝과 방어하기
(Scanning and Defending Networks with Nmap)

 한 컴퓨터 시스템을 크래킹하기 위해서는 우선 계획이 필요하다. 크래커는 목표로 하는 서버를 찾아야 하고, 그 다음에 머신에 어떤 port가 열려있는지, 시스템이 문제를 해결하기 전에 찾아야 한다.

 이것은 일반적으로 스캐닝이라고 하는 것으로, 스캐닝을 통하여 네트워크상에 있는 머신을 찾고, 무슨 port가 열려있는가를 보기 위해 그 머신들을 테스트하는 작업(art)을 하게 된다. 크래커가 공격을 시작하기 전 첫번째 전략은 바로 네트워크와 호스트들을 스캐닝하는 것이다. Nmap과 같은 툴을 이용한 스캐너들 즉, “나쁜 놈들(bad guys)”은 네트워크를 둘러보고 취약한 목표를 찾는다. 한번 이런 목표들이 확인되면, 침입자는 열린 port를 스캔할 수 있다. Nmap은 또한 TCP stack fingerprinting을 이용하여 정확하게 스캔당한 머신의 타입을 결정할 수 있다.

 이 문서는 각각의 네트워크에 대한 크래커의 시야를 파악하고, 보안 관리자가 자신의 사이트를 조사할 수 있도록, Nmap의 사용법에 대해 다루고 있다. 침입자 또한 같은 툴을 이용하고 있으므로, 관리자는 그들처럼 자신의 사이트가 어떻게 보이는지 알 수 있을 것이다. 하나하나 설명해 나가면서 여러분의 시스템 보안에 이 문서가 도움이 될 수 있길 바란다.

 Nmap은 GNU General Public License(GPL)에 속하며, http://www.insecure.org/nmap 에서 무료로 다운로드 받을 수 있다. tar로 묶인 소스화일, 혹은 rpm으로 된 것도 있다. 필자가 이 문서를 쓰는 시점에서 안정버전은 2.12이다. 약간의 그래픽이 첨가된 프로그램도 있지만, 이 문서에서는 command-line에서의 Nmap 사용법에 대해서만 중점을 두겠다.

 Nmap의 사용법은 꽤 단순하다. command-line에서의 Nmap의 옵션(flag)은 -s 를 붙이는데 스캔의 타입에 따라 다르다. 예를 들어 ping scan에서는 “-sP”이다. 옵션 다음에는 목표 호스트 또는 네트워크를 명시해 준다. Nmap의 성능은 루트로 실행했을 때 가장 최고가 된다. 왜냐하면 일반유저는 Nmap이 활용하는 custom packet을 만들 수 있는 능력이 없기 때문이다.

 Nmap은 타겟을 설정함에 있어 매우 유연한 동작을 보인다. 네트워크 주소 값을 지정할 때 /mask를 덧붙여서 써주면 간단하게는 하나의 호스트, 혹은 전체 네트워크를 스캔할 수 있다. 만약 “victim/24″라고 목표를 지정한다면 네트워크 C class를 검색하고, 또한 “victim/16″이라고 한다면 B class를 검색하게 된다.

 덧붙여서 Nmap은 wild cards(*)를 이용하여 네트워크 검색이 가능하다. 예를 들어 192.168.7.* 는 192.168.7.0/24 이라고 했을 때와 같은 결과 값을 얻을 수 있게 된다. 혹은 192.168.7,1,4,8-12 와 같이 subnet에 있는 호스트를 선택적으로 스캔하는 것도 가능하다.

 * Ping Sweeping

 침입자들은 Nmap을 가지고 전체 네트워크의 타겟 들을 훑어볼 수가 있다. 이는 보통 “-sP”라고 써줌으로써 ping scan을 하는 것이다. 일반적으로 Nmap은 ICMP echo와 TCP ACK를 각 호스트에 보내어 검색을 한다. 그러면 Nmap이 보낸 패킷에 반응을 한 호스트들이 나타나게 된다.

pchero@MyNote:~$ nmap -sP 192.168.3.0/24

Starting Nmap 4.53 ( http://insecure.org ) at 2008-08-06 00:38 KST
Host 192.168.3.1 appears to be up.
Host 192.168.3.215 appears to be up.
Nmap done: 256 IP addresses (2 hosts up) scanned in 2.378 seconds

 아마도 때때로 여러분은 단지 ICMP echo request를 보낼 필요없이 시스템이 동작하는지 만을 파악하길 원할 것이다. 이런 경우 목표 네트워크를 스캔하기 위해서 TCP “ping”을 사용한다.

 TCP “ping”은 목표 네트워크의 각각의 머신에게 ACK를 보낸다. 각 머신들은 TCP RST를 이용하여 응답하게 된다. TCP “ping”을 이용한 스캔을 위해서는 “-PT”라는 옵션을 첨가해주면 된다. 이번의 예제에서 80번 tcp port(http)를 target port로 이용해 보도록 하겠다. 이것은 목표의 라우터들 심지어 firewall 조차도 아마 통과할 수 있을 것이다. 주의할 것은 머신이 살아있는지 죽어있는지를 결정하는데 호스트의 target port가 열려있을 필요가 없다는 것이다. 다음에 보이는 것이 실행의 예이다.

pchero@MyNote:~$ sudo nmap -sP -PT80 192.168.3.0/24

Starting Nmap 4.53 ( http://insecure.org ) at 2008-08-06 00:44 KST
Host 192.168.3.1 appears to be up.
MAC Address: 00:11:88:4D:3D:82 (Enterasys)
Host 192.168.3.215 appears to be up.
Nmap done: 256 IP addresses (2 hosts up) scanned in 3.462 seconds

 침입자들은 목표 네트워크의 머신이 살아있는지 알 때, 으레 다음 단계로 port scanning을 한다.

 port scanning의 여러가지 타입들이 Nmap을 통해 제공된다. (TCP connect, TCP SYN, Stealth FIN, Xmas Tree, Null, UDP scan 등)

 * Port Scanning

 Nmap은 connect() system call을 이용해 목표 호스트의 지정한 port에 접속을 하고, TCP의 3-way handshake를 완결 짓기 때문에 TCP 접속을 이용한 침입자는 쉽게 탐지된다. 호스트 머신의 Log 기록들에 침입자에 의해 열려진 port들이 나타날 것이다. TCP connect scan은 -“sT” flag를 이용하여 사용한다.

pchero@MyNote:~$ sudo nmap -sT 192.168.3.0/24

Starting Nmap 4.53 ( http://insecure.org ) at 2008-08-06 00:49 KST
Interesting ports on 192.168.3.1:
Not shown: 1524 filtered ports, 189 closed ports
PORT   STATE SERVICE
23/tcp open  telnet
MAC Address: 00:11:88:4D:3D:82 (Enterasys)

All 1714 scanned ports on 192.168.3.215 are closed

Nmap done: 256 IP addresses (2 hosts up) scanned in 8.599 seconds

 

 * Stealth Scanning
 
 만약 공격자가 목표 머신의 시스템 로그 기록에 그가 요청한 것들을 남기지 않고 스캔하길 원한다면 무엇을 해야 할까? TCP SYN scans는 목표 머신에 log기록을 거의 남기지 않는다. SYN scan은 TCP 연결경로의 첫번째 패킷으로 SYN packet을 보냄으로써 시작한다. 열린 port들은 SYN|ACK 로 응답을 할 것이다. 그러나 이때 공격자가 ACK대신 RST를 전송하면 연결은 종료되게 된다. 장점을 3-way handshake가 절대 완성되는 않는다는 것과 이런 타입의 로그 기록을 남기는 사이트는 거의 없다는 것이다. 한편 닫혀있는 port들은 RST와 함께 처음의 SYN에 응답할 것이고 Nmap은 그 호스트의 port가 닫혀 있는다는 것을 결정할 수 있게 된다. SYN scan은 “-sS” flag를 이용하여 실행한다.

pchero@MyNote:~$ sudo nmap -sS 192.168.3.0/24

Starting Nmap 4.53 ( http://insecure.org ) at 2008-08-06 00:54 KST
Interesting ports on 192.168.3.1:
Not shown: 1712 closed ports
PORT   STATE    SERVICE
22/tcp filtered ssh
23/tcp open     telnet
MAC Address: 00:11:88:4D:3D:82 (Enterasys)

All 1714 scanned ports on 192.168.3.215 are closed

Nmap done: 256 IP addresses (2 hosts up) scanned in 27.168 seconds

 비록 SYN scan이 더 알아챌 수 없을지라도, 몇 가지 침입 탐지 대책들을 이용하여 탐지해 낼 수 있다. Stealth FIN, Xmas Tree, 그리고 Null scan들은 packet filter나 firewall 들을 피하는데 사용되어 진다. 이런 3가지 스캔들은 닫힌 port들을 위해 RST를 리텀하고, 열린 포트들을 packet를 버린다. FIN “-sF” 스캠은 FIN packet을 각각의 port로 보낼 것이고, 한편 Xmas Tree scan “-sX”는 FIN, URG, PUSH flag를 모두 켠다. 그리고 Null scan “-sN”은 모든 flag를 끈다.
 TCP 표준들에 Microsoft는 순종하고 있지 않기 때문에 FIN, Xmas Tree, 그리고 Null scan은 오직 Microsoft 운영체제가 아닌 경우에서만 영향을 발휘한다.

 * UDP Scanning

 만약 침입자가 exploit(역자 주 : 크래커들이 시스템을 칩임하기 위해 사용하는 특정한 프로그램들을 지칭함)를 위해 특정 rpcbind hole 이나 cDc Back Orifice 같은 UDP hole을 찾는다면, 그/그녀는 어떤 UDP port가 열려있는지 알기를 원할 것이다.
 침입자는 UDP scan “-sU”를 사용하여 호스트에 어떤 UDP port가 열려있는지 알 수 있다. 만약 호스트에서 “port unreachable” 메시지를 리턴하면 그 포트는 닫혀있는 것으로 간주된다. 대부분의 UNIX 호스트들은 ICMP 에러의 비율을 제한하고 있기 때문에 이런 방법은 시간을 많이 소비하게 된다. 다행히도 Nmap은 이런 비율을 탐지하고 그 자신의 속도를 낮추어 거부했을 때의 메시지로 인해 목표가 오버플로어 되지 않도록 한다. 다음은 UDP scan의 실행결과이다.

pchero@MyNote:~$ sudo nmap -sU 192.168.3.0/24

Starting Nmap 4.53 ( http://insecure.org ) at 2008-08-06 01:02 KST
Interesting ports on 192.168.3.1:
Not shown: 1485 closed ports
PORT    STATE         SERVICE
67/udp  open|filtered dhcps
161/udp open|filtered snmp
520/udp open|filtered route
MAC Address: 00:11:88:4D:3D:82 (Enterasys)

Interesting ports on 192.168.3.215:
Not shown: 1486 closed ports
PORT     STATE         SERVICE
68/udp   open|filtered dhcpc
5353/udp open|filtered zeroconf

Nmap done: 256 IP addresses (2 hosts up) scanned in 8.846 seconds

 * OS Fingerprinting

 종종 침입자는 특정한 플랫폼에서만 실행되는 exploit를 실행하기 위해 그와 유사한 윤영체제를 찾을 것이다. 일반적으로 TCP/IP fingerprinting은 “-O”옵션을 포함하여 원격으로 운영체제를 탐지해낸다. 이것은 ping scan을 제외한 port scan과 결합하여 사용해야만 한다. Nmap은 호스트에 다른 타입의 조사를 행하여 OS를 찾아낸다. 원격으로 OS를 탐지해내기 위한 다른 방법과 마찬가지로 ICP initial Sequence Number(ISN)의 패턴을 찾기 위해 SYN packet과 함께 선언하지 않은 flag를 리모트 호스트로 보내고, BOGUS flag는 원격 호스트의 그 반응이 어떤 종류인가를 입증하기 위해 FIN 조사같은 기능을 사용한다. 그것은 TCP stack에서 포함하고 있고 이를 Fingerprinting하게 된다. 본 문서에서 stack fingerprinting 까지 논의하는 것은 한계가 있으므로 관심이 있는 사람은 Nmap의 저자인 Fyodor에 의해 쓰여진 문서를 찾아보기 바란다.
(http://www.insecure.org/nmap/nmap-fingerprinting-aricle.html)

 Nmap의 OS 탐지기능의 특징은 매우 정확하며, SYN scan을 포함하여 솔라리스 2.7 머신의 stack을 fingerprinting해서 증명하는 것과 같은 일에 효과적인 도구이다.

# nmap -sS -O
192.168.7.12
Starting nmap V. 2.12 by Fyodor
(fyodor@dhp.com, www.insecure.org/nmap/)
Interesting
ports on comet (192.168.7.12):
Port    State
      Protocol  Service

7        open
       tcp         echo

9        open
       tcp         discard

13      open        tcp
        daytime

19      open        tcp
        chargen

21      open        tcp
        ftp


TCP Sequence Prediction: Class=random positive
increments
                         Difficulty=17818
(Worthy challenge)
Remote operating system guess:
Solaris 2.6 – 2.7
Nmap run completed — 1 IP
address (1 host up) scanned in 5 seconds

 TCP Sequence Prediction 부분을 주의해서 보기 바란다. -O 옵션을 주어 Nmap을 실행시키게 되면 TCP Sequence Prediction이 얼마나 어려운지도 알려준다. 이것은 침입자가 호스트의 공격할 수 있는 위험성에 대한 정도로써 얼마나 침입할 가치가 있는가를 포함하고 있기도 하다.

 * Ident Scanning

 침입자는 종종 root로 돌아가는 web server와 같은 프로세스를 공격 대상으로 삼을 때가 있다. 만약 타겟이 identd를 돌리고 있다면 침입자는 Nmap을 사용하여 httpd 데몬이 어떤 소유자로 실행되고 있는지 찾아낼 수 있다. 이는 TCP connect scan에 “-I”옵션을 포함하여 실행한다. 아래에 한 리눅스 웹서버를 스캔 한 예가 있다.

# nmap -sT -p
80 -I -O www.yourserver.com
Starting nmap V.
2.12 by Fyodor (fyodor@dhp.com, www.insecure.org/nmap/)

Interesting ports on www.yourserver.com (xxx.xxx.xxx.xxx):

Port    State       Protocol
 Service         Owner

80      open        tcp
       http            root

TCP Sequence Prediction: Class=random positive increments

                          Difficulty=1140492
(Good luck!)
Remote operating system guess:
Linux 2.1.122 – 2.1.132; 2.2.0-pre1 – 2.2.2

Nmap run completed — 1 IP address (1 host up) scanned
in 1 second

  만약 당신의 웹서버가 실수로 root로 실행되도록 설정되었다면, 당신에게 회사에세 밤샐 일이 생길지도 모근다.

 루트로 아파치를 실행시키는 것은 나쁜 보안 습관이다. /etc/inetd.conf 에서 auth 부분을 주석 처리해서 ident 요청을 막아두어야 한다. 그리고 inetd를 재 실행 시킨다. ident 요청을 막는 다른 방법은 ipchains나 다른 firewall을 이용하여 당신의 네트워크에 장벽을 설치하는 것이다. 이것은 어떤 데몬이 누구의 소유로 동작하는지 당신의 사이트를 조사해보려는 호기심 많은 무뢰한들을 막을 수 있을 것이다.

 * Options

 이런 scan들에 덧붙여서 Nmap이 제공하는 무수한 옵션들을 소개하겠다. 그 중 하나가 “-PT”이다. 우리는 이미 위에서 다루었다. 이 옵션은 타겟 머신이나 네트워크의 일반적으로 filter되지 않은 port들을 TCP ping으로 직접 ping scan할 때 사용된다. 유용한 옵션으로 “-Po”가 있다. Nmap은 port scan을 하기전에 TCP “ping”과 ICMP echo 모두를 이용하여 타겟을 ping 할 것이다. 만약 사이트에서 ICMP와 TCP 검색을 막아 놓았을 경우 보통 스캔되지 않을 것이다. 그래서 “-Po” 옵션을 이용하면 처음에 ping을 하지 않고 호스트를 스캔 하도록 Nmap에서 지원하고 있다.

 한가지, 독자가 한번 사용하면 습관처럼 사용하게 될 옵션이 있다. 바로 “-v” 자세한 옵션이다. 이 옵션은 모든 스캔 타입들과 함께 사용할 수 있다. 이 옵션을 한번 혹은 두 번 사용해서 타겟 머신에 관한 보다 더 자세한 정보를 얻을 수 있다.

 특정한 port를 타겟으로 삼는 기능은 “-p”옵션을 이용하여 실행된다. 예를 들어 침입자가 당신의 웹서버를 ftp(port 21), telnet(port 23), name service(port 53), 그리고 http(port 80)에 관해 조사하고, 어떤 OS를 쓰는지 까지 알길 원한다면 다음과 같은 SYN scan을 이용할 것이다.

 # nmap -sS -p
21,23,53,80 -O -v www.yourserver.com

 * 결론

 침입자가 Nmap을 사용하여 어떤 측정을 했겠는가? Scanlogd, Countney, 그리고 Shadow와 같은 수많은 툴 들이 존재한다. 그러나 이런 툴을 사용하는 것이 실무에서 일하는 관리자들의 지식을 대신할 순 없다. 스캔은 종종 침입의 전조로 나타나기 때문에 사이트들은 그들의 머신에 대해 감시와 제어를 우선시해야 한다.

 Nmap을 이용해 각자의 사이트를 감시함으로서, 시스템과 네트워크 관리자들은 잠재적인 침입자들이 당신의 사이트를 조사하는 것들을 발견할 수 있다. 자,,이제 당신의 라우터들 구석에 Nmap과 함께 리눅스 박스를 설치하고, 펭귄 군단의 하나로써 스캐닝을 시작하자!.

 출처 : http://www.linuxlab.co.kr/docs/00-05-2.htm

Md5sum 사용하기

 * Md5sum 사용하기

 때때로, 개발자들은 md5sum 명령을 사용해서 파일의 해시값을 계산한다. 이 해시값과, md5sum 명령을 사용해서 파일이 수정되지 않았다는 것을 확인할 수 있다. 확인하는 가장 쉬운 방법은, 먼저 개발자가 계산한 해시값을 다운로드 받고, 실제 실행 파일을 다운로드 받은 다음, 실행 파일의 해시값을 md5sum으로 계산해서 개발자가 계산한 값과 비교해 보는 것이다.

 예를 들어, 당신의 시스템에 설치된 vsftpd에 보안 문제점이 있다고 하자. 당신은 이런 문제점을 해결한 최신 버전을 다운로드 하고 싶다. 웹 사이트에서 이 최신 버전을 다운로드 한 다음, 이 파일의 해시 값을 md5sum을 사용해서 계산한다.

pchero@MyNote:~$ md5sum vsftpd-1.1.3-8.i386.rpm
d2e807f808c45407f08528f50d29933b  vsftpd-1.1.3-8.i386.rpm

 그런데 만약 계산한 결과 값과 개발자가 올려놓은 md5sum 해시 값이 서로 다르다고 한다면 당신은 방금 다운로드 받은 파일이 개발자가 만 것이 아님을 알 수 있다.

GNU Privacy Guard

 * GNU Privacy Guard

 GNU Privacy Guard(GPG)는 가장 많이 사용되는 오픈 소스 툴의 하나이다. 이 툴은 www.gnupg.org에서 다운 받을 수 있다. 여기서는 모든 Unix 버전용 실행파일과 소스 코드를 다운받을 수 있다. GPG를 사용하면 파일과 e-mail 메시지를 암호화할 수 있다. 또한 공개키들을 import 하거나 export 할 수 있다. 이렇게 하면, PGP 및 GPG에서 생성한 키들을 검사해서, 다운로드 받은 tarball 파일과 RPM 파일이 올바른 파일인지 확인할 수 있다.

 * GNU Privacy Guard 사용하기.
 먼저 GPG가 설치되어 있는지를 검사한다.

pchero@MyNote:~$ whereis gpg
gpg: /usr/bin/gpg /usr/share/man/man1/gpg.1.gz

 이 때 GPG가 설치돼 있지 않다면, GPG를 www.rpmfind.net이나 www.gnupg.org/download.html에서 다운로드 후, 설치해야 한다.

 만약 데비안 혹은 우분투 시스템일 경우

$ sudo apt-get install gpg

 라고 입력한다.

 프로그램이 설치됐다면, 처음 할 일은 루트가 아닌 사용자의 메모리를 보호하는 것이다. 대부분의 리눅스 시스템에서 GPG는 SUID root 로 실행시켜야 한다. 모든 애플리케이션은 시스템에 메모리를 할당하는데, GPG는 이 메모리를 안전하게 보호해야 한다. 메모리가 보호받지 못한다면, 악의적인 사용자가 메모리 정보를 캡쳐한 다음 당신이 암호화하려는 내용의 정보를 빼낼 수 있을 것이다. 이렇게 메모리를 보호하기 위해, GPG는 메모리를 사용하기 전에 먼저 잠그는(lock) 일을 한다. 메모리를 잠그려면 root 권한이 필요하다. 이 메모리를 잠근 뒤에, GPG는 프로그램을 실행시킨 사람의 권한으로 돌아간다.

애플리케이션을 SUID root로 실행시킨다는 말은, 애플리케이션을 실행시킨 사용자가 root가 아니라도 root권한으로 실행시킨다는 뜻이다.

 그러나 기본적으로 GPG는 SUID root로 설치되지 않는다.
 이것을 setuid root로 만들려면 다음과 같이 한다.

1. 애플리케이션을 찾는다.(우분투의 경우 /usr/bin/gpg에 있다.)
2. su – 명령으로 root 계정으로 로그인한다.
3. chmod u+s /usr/bin/gpg 명령을 실행시킨다.

 그리고 다음의 명령을 실행해서 GPG동작에 필요한 디렉토리와 파일들을 생성해야 한다.

$ gpg –gen-key

 그 후, 다음의 명령어로 키 쌍(key pair)을 생성해야 한다.

$ gpg –genkey

 그러면 GPG는 키 유형을 선택하라고 물어볼 것이다. 여기서 선택할 수 있는 것은

 Digital Signature Algorithm(DSA)
 ElGamal(기본 알고리즘)
 DSA(서명만)
 ElGamal(서명과 암호화)

이다. 이들 옵션은 서로 다른 서명 및 암호화 알고리즘을 사용한다. 첫번째 옵션에서는 표준 Elgamal 키 분배 방법과 서로 다른 서명 및 암호화 알고리즘을 사용한다. 첫번째 옵션에서는 표준 Elgamal 키 분배 방법과 DSA를 사용하는데, 이를 이용해 서명을 하거나 데이터를 암호화한다.
 DSA는 기존에 사용하던 RSA와는 달리, 사용에 제한이 없다. 여기서 만약 문서를 서명하고 암호화하고 싶다면 DSA를 사용하면 된다. 대부분의 사람을 정보를 서명하고 암호화하는데 첫번째 옵션을 사용한다. 일반적으로, 첫번째(기본값)를 선택하는 게 좋다.

 그 다음, 키의 크기를 선택해야 한다. 키 크기의 기본값은 2048 bit인데, 보통은 이정도로 충분하다.

 다음은 키 만료 시간을 정하는 부분이다. 키가 1년까지만 유효하도록 설정하려면 1y를 입력하면 된다.

 마지막으로 유저 정보를 입력하는 부분이다.

 Real name : 이름을 입력하는 부분
 Email address : E-Mail 주소를 입력하는 부분
 Comment : 원하는 정보를 넣는다. (ex. GPG Signature)…

 그리고 입력한 정보가 맞는지 확인을 한 다음 비밀키를 생성하기 위한 passphrase를 입력한다. 이 passphrase는 최소한 6글자 이상이 되어야 하며, 물론 당연히 기억할 수 있어야 한다. 이 작업이 끝나면 GPG는 새 개인키를 생성한다. GPG가 끝나면 키가 생성됐고 서명됐다는 메시지를 받을 것이다.

 이제 GPG에서 계정키를 제대로 만들고 서명했는지 확인하는 데는 다음 명령을 실행한다.

 $ gpg –list-secert-key
 $ gpg –list-public-key
 $ gpg –list-sig

 위 명령들은 각각 비밀키와 공개키를 나열하고, 서명을 나열한다.

 다음으로 할 일은 취소 인증서(revocation certificate)를 만들어야 한다. 취소 인증서는, 비밀키를 삭제하고 새로 만들고 싶을 때 사용하는 인증서이다. 취소 인증서를 만드는 과정은 다음과 같다.

pchero@MyNote:~$ gpg –output revoke.asc –gen-revoke pchero21@gmail.com

sec  1024D/68B8F353 2007-11-15 Kim Sung-tae <pchero21@gmail.com>

Create a revocation certificate for this key? (y/N) y
Please select the reason for the revocation:
  0 = No reason specified
  1 = Key has been compromised
  2 = Key is superseded
  3 = Key is no longer used
  Q = Cancel
(Probably you want to select 1 here)
Your decision? 1
Enter an optional description; end it with an empty line:
>
Reason for revocation: Key has been compromised
(No description given)
Is this okay? (y/N) y

You need a passphrase to unlock the secret key for
user: “Kim Sung-tae <pchero21@gmail.com>”
1024-bit DSA key, ID 68B8F353, created 2007-11-15

ASCII armored output forced.
Revocation certificate created.

Please move it to a medium which you can hide away; if Mallory gets
access to this certificate he can use it to make your key unusable.
It is smart to print this certificate and store it away, just in case
your media become unreadable.  But have some caution:  The print system of
your machine might store the data and make it available to others!

 키 쌍과 취소 인증서를 만들었다면, 이제 키를 import 혹은 export할 수 있다. 키를 export 하려면 다음 명령을 실행한다.

 $ gpg –export –armor > yourname.asc

 위 명령은 사용자의 공개키를 담은 파일을 생성하는 명령어이다. 이제 이 공개키를 신뢰관계를 맺고자 하는 사람에게 보내주면 된다.

 그럼, 이제는 RPM 명령을 사용해서 다른 사람이 만든 서명과 공개키를 확인하는 방법을 알아보자.

 먼저 다운로드받은 RPM 패키지가 Red Hat에서 만든 것인지를 확인하기 위해, Red Hat의 서명을 다운 받아야 한다. www.redhat.com으로 간 다음 Red Hat의 공개키를 받고, 원하는 RPM 파일도 다운로드 받는다.

  redhat의 공개키는 다음의 주소에 있다.
http://www.redhat.com/security/team/key/

 버전에 맞는 공개키를 다운받고 다음을 실행한다. 마지막에 쓴 redhat.asc는 파일명이다. 다른 파일명으로 redhat의 공개키를 저장했다면 마지막 부분을 파일명에 맞게 고쳐 써야 한다.

$ gpg –import redhat.asc

 키를 import 했다면 이제 키를 서명해야 한다. 만약 이 키를 서명하지 못하면, 나중에 이 키를 사용하려 할 때 에러가 날 것이다. 키를 서명하려면 gpg –sign 명령을 이용하거나 gpg –edit-key security@redhat.com 명령을 이용하여 서명한다.

pchero@MyNote:~$ gpg –edit-key security@redhat.com
gpg (GnuPG) 1.4.6; Copyright (C) 2006 Free Software Foundation, Inc.
This program comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it
under certain conditions. See the file COPYING for details.

pub  1024D/DB42A60E  created: 1999-09-23  expires: never       usage: SCA
                     trust: unknown       validity: unknown
sub  2048g/961630A2  created: 1999-09-23  expires: never       usage: E  
[ unknown] (1). Red Hat, Inc <security@redhat.com>

명령> sign

pub  1024D/DB42A60E  created: 1999-09-23  expires: never       usage: SCA
                     trust: unknown       validity: unknown
 Primary key fingerprint: CA20 8686 2BD6 9DFC 65F6  ECC4 2191 80CD DB42 A60E

     Red Hat, Inc <security@redhat.com>

Are you sure that you want to sign this key with your
key “Kim Sung-tae <pchero21@gmail.com>” (68B8F353)

Really sign? (y/N) y

You need a passphrase to unlock the secret key for
user: “Kim Sung-tae <pchero21@gmail.com>”
1024-bit DSA key, ID 68B8F353, created 2007-11-15

명령> q
Save changes? (y/N) y

 그리고 다운받은 RPM을 확인하는데는 다음의 명령어를 사용한다.

 $ rpm -Kv your_rpm.9386.rpm

Openssl을 이용한 암호화 통신

 Openssl을 이용한 암호화 통신

 서버는 암호화 통신을 위하여 가장 먼저 SSL_CTX와 SSL 구조체를 선언하여 암호화 통신을 위한 정보를 관리할 수 있도록 한다. 그리고, SSL 세션을 시작하기 위한 초기값을 설정하고, SSL_CTX_new() 함수를 이용하여 SSL 컨텍스트를 생성한다.

 다음 단계는 인증서를 이용하여 서버와 클라이언트 간의 인증을 수행할 경우 SS_CTX_use_certificate_file()를 이요하여 인증서 파일을 생성하며, SSL_CTX_use_PrivateKey_file()를 이용하여 개인 키를 생성한다.

 이런 준비 단계 후에 SSL_new() 함수를 이용하여 SSL 세션을 생성하고, 이후의 단계는 기존의 소켓 서버와 유사한 방법으로 처리를 수행할 수 있다. SSL을 이용한 송수신은 SSL_accept(), SSL_read(), SSL_write() 등의 함수를 이용할 수 있으며, 이 과정은 기종의 소켓 프로그래밍과 유사한 방법으로 수행된다.

serv.c (Language : c)
  1. /*
  2. *      serv.c
  3. *     
  4. *      Copyright 2008 Kim Sung-tae <pchero21@gmail.com>
  5. *     
  6. *      This program is free software; you can redistribute it and/or modify
  7. *      it under the terms of the GNU General Public License as published by
  8. *      the Free Software Foundation; either version 2 of the License, or
  9. *      (at your option) any later version.
  10. *     
  11. *      This program is distributed in the hope that it will be useful,
  12. *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. *      GNU General Public License for more details.
  15. *     
  16. *      You should have received a copy of the GNU General Public License
  17. *      along with this program; if not, write to the Free Software
  18. *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  19. *      MA 02110-1301, USA.
  20. */
  21.  
  22.  
  23. #include <stdio.h>
  24. #include <unistd.h>
  25. #include <stdlib.h>
  26. #include <memory.h>
  27. #include <errno.h>
  28. #include <sys/types.h>
  29. #include <sys/socket.h>
  30. #include <netinet/in.h>
  31. #include <arpa/inet.h>
  32. #include <netdb.h>
  33.  
  34. /*
  35. * openssl 관련 헤더 파일을 include 한다.
  36. */
  37. #include <openssl/rsa.h>        /* SSLeay stuff */
  38. #include <openssl/crypto.h>
  39. #include <openssl/x509.h>
  40. #include <openssl/pem.h>
  41. #include <openssl/ssl.h>
  42. #include <openssl/err.h>
  43.  
  44. /* define HOME to be dir for key and cert files…. */
  45. #define HOME “./”
  46.  
  47. /* Make these what you want for cert & key files */
  48. #define CERTF HOME “server.crt”
  49. #define KEYF HOME “server.key”
  50.  
  51. #define CHK_NULL(x) if((x) == NULL) exit(1);
  52. #define CHK_ERR(err, s) if((err) == -1) { perror(s); exit(1); }
  53. #define CHK_SSL(err) if((err) == -1) { ERR_print_errors_fp(stderr); exit(2); }
  54.  
  55.  
  56. int main(void)
  57. {
  58.     int err;
  59.     int listen_sd;
  60.     int sd;
  61.     struct sockaddr_in sa_serv;
  62.     struct sockaddr_in sa_cli;
  63.     size_t client_len;
  64.    
  65.     /* SSL Context 및 관련 구조체를 선언한다. */
  66.     SSL_CTX  *ctx;
  67.     SSL    *ssl;
  68.     X509                *client_cert;
  69.     char                *str;
  70.     char                buf[4096];
  71.     SSL_METHOD  *meth;
  72.    
  73.     /* SSL 관련 초기화 작업을 수행한다. */
  74.     SSL_load_error_strings();
  75.     SSLeay_add_ssl_algorithms();
  76.     meth = SSLv23_server_method();    // 서버 메소드.
  77.     ctx = SSL_CTX_new(meth);                // 지정된 초기 값을 이용하여 SSL Context를 생성한다.
  78.    
  79.     if(!ctx) {
  80.         ERR_print_errors_fp(stderr);
  81.         exit(2);
  82.     }
  83.    
  84.     /* 사용하게 되는 인증서 파일을 설정한다. */
  85.     if(SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0) {      // 인증서를 파일로 부터 로딩할때 사용함.
  86.         ERR_print_errors_fp(stderr);
  87.         exit(3);
  88.     }
  89.    
  90.     /* 암호화 통신을 위해서 이용하는 개인 키를 설정한다. */
  91.     if(SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0) {
  92.         ERR_print_errors_fp(stderr);
  93.         exit(4);
  94.     }
  95.    
  96.     /* 개인 키가 사용 가능한 것인지 확인한다. */
  97.     if(!SSL_CTX_check_private_key(ctx)) {
  98.         fprintf(stderr, “Private key does not match the certificate public keyn);
  99.         exit(5);
  100.     }
  101.    
  102.     /* Prepare TCP socket for receiving connections */
  103.     listen_sd = socket(AF_INET, SOCK_STREAM, 0);
  104.     CHK_ERR(listen_sd, “socket”);
  105.    
  106.     memset(&sa_serv, , sizeof(sa_serv));
  107.     sa_serv.sin_family = AF_INET;
  108.     sa_serv.sin_addr.s_addr = INADDR_ANY;
  109.     sa_serv.sin_port = htons(1111); /* Server Port number */
  110.  
  111.     err = bind(listen_sd, (struct sockaddr*)&sa_serv, sizeof(sa_serv));
  112.     CHK_ERR(err, “bimd”);
  113.    
  114.     /* Receive a TCP connection. */
  115.      err = listen(listen_sd, 5);
  116.      CHK_ERR(err, “listen”);
  117.     
  118.      client_len = sizeof(sa_cli);
  119.      sd = accept(listen_sd, (struct sockaddr*)&sa_cli, &client_len);
  120.      CHK_ERR(sd, “accept”);
  121.      close(listen_sd);
  122.     
  123.      printf(“Connection from %1x, port %xn, sa_cli.sin_addr.s_addr, sa_cli.sin_port);
  124.     
  125.      /* TCP connection is ready. Do server side SSL. */
  126.     ssl = SSL_new(ctx); // 설정된 Context를 이용하여 SSL 세션의 초기화 작업을 수행한다.
  127.     CHK_NULL(ssl);
  128.     SSL_set_fd(ssl, sd);
  129.     err = SSL_accept(ssl);    // SSL 세션을 통해 클라이언트의 접속을 대기한다.
  130.     CHK_SSL(err);
  131.    
  132.     /* Get the cipher – opt */
  133.     printf(“SSL connection using %sn, SSL_get_cipher(ssl));
  134.    
  135.     /* 클라이언트의 인증서를 받음 – opt */
  136.     client_cert = SSL_get_peer_certificate(ssl);
  137.     if(client_cert != NULL) {
  138.         printf(“Client certificate:n);
  139.        
  140.         str = X509_NAME_oneline(X509_get_subject_name(client_cert), 0, 0);
  141.         CHK_NULL(str);
  142.         printf(t subject: %sn, str);
  143.         OPENSSL_free(str);
  144.        
  145.         str = X509_NAME_oneline(X509_get_issuer_name(client_cert), 0, 0);
  146.         CHK_NULL(str);
  147.         printf(t issuer: %sn, str);
  148.         OPENSSL_free(str);
  149.        
  150.         /* We could do all sorts of certificate verification stuff here before deallocating the certificate. */
  151.         X509_free(client_cert);
  152.     } else {
  153.         printf(“Client does not have certificate.n);
  154.     }
  155.    
  156.     /* SSL 세션을 통해서 클라이언트와 데이터를 송수신한다. */
  157.     err = SSL_read(ssl, buf, sizeof(buf)1);
  158.     CHK_SSL(err);
  159.     buf[err] = ;
  160.     printf(“Got %d chars: ‘%s’n, err, buf);
  161.    
  162.     err = SSL_write(ssl, “I hear you/”, strlen(“I hear you.”));
  163.     CHK_SSL(err);
  164.    
  165.     /* 설정한 자원을 반환하고 종료한다. */
  166.     close(sd);
  167.     SSL_free(ssl);
  168.     SSL_CTX_free(ctx);
  169.    
  170.     return(0);
  171. }
  172.  

cli.c (Language : c)
  1. /*
  2. *      cli.c
  3. *     
  4. *      Copyright 2008 Kim Sung-tae <pchero21@gmail.com>
  5. *     
  6. *      This program is free software; you can redistribute it and/or modify
  7. *      it under the terms of the GNU General Public License as published by
  8. *      the Free Software Foundation; either version 2 of the License, or
  9. *      (at your option) any later version.
  10. *     
  11. *      This program is distributed in the hope that it will be useful,
  12. *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. *      GNU General Public License for more details.
  15. *     
  16. *      You should have received a copy of the GNU General Public License
  17. *      along with this program; if not, write to the Free Software
  18. *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  19. *      MA 02110-1301, USA.
  20. */
  21.  
  22.  
  23. #include <stdio.h>
  24. #include <memory.h>
  25. #include <errno.h>
  26. #include <sys/types.h>
  27. #include <sys/socket.h>
  28. #include <netinet/in.h>
  29. #include <arpa/inet.h>
  30. #include <netdb.h>
  31.  
  32. #include <openssl/crypto.h>
  33. #include <openssl/x509.h>
  34. #include <openssl/pem.h>
  35. #include <openssl/ssl.h>
  36. #include <openssl/err.h>
  37.  
  38. #define CHK_NULL(x) if((x) == NULL) exit(1);
  39. #define CHK_ERR(err, s) if((err) == -1) { perror(s); exit(1); }
  40. #define CHK_SSL(err) if((err) == -1) { ERR_print_errors_fp(stderr); exit(2); }
  41.  
  42.  
  43. int main(void)
  44. {
  45.     int err;
  46.     int sd;
  47.     struct sockaddr_in sa;
  48.    
  49.     /* SSL 관련 정보를 관리할 구조체를 선언한다. */
  50.     SSL_CTX   *ctx;
  51.     SSL     *ssl;
  52.     X509                    *server_cert;
  53.     char                    *str;
  54.     char                    buf[4096];
  55.     SSL_METHOD    *meth;
  56.    
  57.     /* 암호화 통신을 위한 초기화 작업을 수행한다. */
  58.     SSL_load_error_strings();
  59.     SSLeay_add_ssl_algorithms();
  60.     meth = SSLv3_client_method();
  61.     ctx = SSL_CTX_new(meth);
  62.     CHK_NULL(ctx);
  63.    
  64.     /* 사용하게 되는 인증서 파일을 설정한다. – opt*/
  65.     if(SSL_CTX_use_certificate_file(ctx, “./client.crt”, SSL_FILETYPE_PEM) <= 0) {    // 인증서를 파일로 부터 로딩할때 사용함.
  66.         ERR_print_errors_fp(stderr);
  67.         exit(3);
  68.     }
  69.    
  70.     /* 암호화 통신을 위해서 이용하는 개인 키를 설정한다. – opt */
  71.     if(SSL_CTX_use_PrivateKey_file(ctx, “./client.key”, SSL_FILETYPE_PEM) <= 0) {
  72.         ERR_print_errors_fp(stderr);
  73.         exit(4);
  74.     }
  75.    
  76.     /* 개인 키가 사용 가능한 것인지 확인한다. – opt */
  77.     if(!SSL_CTX_check_private_key(ctx)) {
  78.         fprintf(stderr, “Private key does not match the certificate public keyn);
  79.         exit(5);
  80.     }
  81.    
  82.     //CHK_SSL(err);
  83.    
  84.     /* Create a socket and connect to server using normal socket calls. */
  85.     sd = socket(AF_INET, SOCK_STREAM, 0);
  86.     CHK_ERR(sd, “socket”);
  87.    
  88.     memset(&sa, , sizeof(sa));
  89.     sa.sin_family = AF_INET;
  90.     sa.sin_addr.s_addr = inet_addr(“127.0.0.1”);        // Server IP Address
  91.     sa.sin_port = htons(1111);                // Server Port Number
  92.    
  93.     err = connect(sd, (struct sockaddr*)&sa, sizeof(sa));
  94.     CHK_ERR(err, “connect”);
  95.    
  96.     /* Now we have TCP connection. Start SSL negotiation. */
  97.     ssl = SSL_new(ctx);  // 세션을 위한 자원을 할당받는다.
  98.     CHK_NULL(ssl);
  99.    
  100.     SSL_set_fd(ssl, sd);
  101.     err = SSL_connect(ssl); // 기존의 connect() 함수 대신 사용하여 서버로 접속한다.
  102.     CHK_NULL(err);
  103.    
  104.     /* Following two steps are optional and not required for data exchange to be successful. */
  105.    
  106.     /* Get the Cipher – opt */
  107.     printf(“SSL connection using %sn, SSL_get_cipher(ssl));
  108.    
  109.     /* Get server’s certificate (note: beware of dynamic allocation) – opt */
  110.     /* 서버의 인증서를 받는다. */
  111.     server_cert = SSL_get_peer_certificate(ssl);
  112.     CHK_NULL(server_cert);
  113.     printf(“Server certificate:n);
  114.    
  115.     /* 인증서의 이름을 출력한다. */
  116.     str = X509_NAME_oneline(X509_get_subject_name(server_cert), 0, 0);
  117.     CHK_NULL(str);
  118.     printf(t subject: %sn, str);
  119.     OPENSSL_free(str);
  120.    
  121.     /* 인증서의 issuer를 출력한다. */
  122.     str = X509_NAME_oneline(X509_get_issuer_name(server_cert), 0, 0);
  123.     CHK_NULL(str);
  124.     printf(t issuer: %sn, str);
  125.     OPENSSL_free(str);
  126.    
  127.     /* We could do all sorts of certificate verification stuff here before deallocating the certificate */
  128.     X509_free(server_cert);
  129.    
  130.     /* 서버와 데이터를 송수신 한다. */
  131.     err = SSL_write(ssl, “Hello World!”, strlen(“Hello World!”));
  132.     CHK_SSL(err);
  133.    
  134.     err = SSL_read(ssl, buf, sizeof(buf)1);
  135.     CHK_SSL(err);
  136.     buf[err] = ;
  137.     printf(“Got %d chars: ‘%s’n, err, buf);
  138.     SSL_shutdown(ssl);    // SSL로 연결된 접속을 해지한다.
  139.    
  140.     /* 할당된 자원을 반환하고 종료한다. */
  141.     close(sd);
  142.     SSL_free(ssl);
  143.     SSL_CTX_free(ctx);
  144.    
  145.     return 0;
  146. }
  147.  
  148.  

SSL 라이브러리의 활용

  SSL 라이브러리의 활용

 SSL 라이브러리의 가장 중요한 기능은 암호화 채널을 통한 통신이다. 이를 위해서 각각의 통신은 인증서를 필요한 경우가 있으며, 실제 전자상거래나 증권 거래 등에서 이용하는 인증서인 경우 공인 인증기관에서 발급받는다.

 하지만 제한적인 범위 내에서는 자체적으로 인증서를 생성하여 이용할 수 있으며, 이러한 기능은 openssl 라이브러리에서 기본적인 명령으로 제공한다.

 1) 인증서 만들기
 인 증서(Certificate)는 서버와 클라이언트 간에 통신을 수행하는 과정에서 서로의 신원을 보증하는 일종의 보증서 역할을 한다. 은증서는 공인된 인증기관에서 개인의 신원 확인을 거쳐서 발급하며 주식거래, 전자상거래 같은 금융 거래에서 주로 이용한다. 인증서를 발급하는 기관을 CA(Certificate Authority)라고 하며, 이러한 기관에서 발급한 인증서는 기본적으로 다음과 같은 정보를 포함한다.

 – 소유자 이름 : 주민등록증의 이름과 동일한 기능
 – 일련 번호 : 주민등록증에 비교할 때 주민등록번호의 역할
 – 소유자의 공개 키 : 해당 인증서를 소유한 사람의 공개 키
 – 인증 기관의 전자 서명 : 인증서를 발급한 기관의 전자 서명

 하 지만 개발자 입장에서 제한된 시스템과 응용 프로그램을 대상으로 하는 경우에는 자체적인 인증서를 발급하고 이를 이용하여 응용 프로그램에서 이용할 수도 있는데, 이러한 기능을 수행하는 시스템을 ‘self-signed’ 서버라고 한다. 이를 위해 openssl은 기본적인 명령어로 인증서를 발급하는 기능을 갖추고 있다.

 * 비밀 키 생성
 인증서를 만들 때 가장 먼저 하는 작업으로 비밀 키를 생성한다. 비밀 키는 자신만이 알 수 있게 주의해서 관리해야 하는 정보다. 따라서 가능하면 누구도 알 수 없게 비밀스럽게 생성하고 관리해야 한다. 이 과정에서 가장 먼저 하는 작업은 DSA Parameter를 만드는 과정으로 다음과 같은 명령으로 수행한다.

     # openssl dsaparam 1024 -out dsaparam1024.pem

 이 명령은 1024 비트 크기로 DSA Parameter를 만드는 기능을 수행하며, DSA 키를 만드는 데 사용한다.

 다음 명령은 각각 암호화된 DSA 또는 암호화 되지 않은 DSA 키를 생성하는 기능을 한다. 처음의 것이 암호화되지 않은 키를 생성하는 명령이고, 두번째 명령이 암호화 된 DSA 키를 생성하는 명령이다.

     # openssl gendsa -out dsa1024.pem -des dsaparam1024.pem
     # openssl gendsa -out dsa1024.pem dsaparam1024.pem

 RSA 키를 생성하려면 다음과 같은 형식으로 명령을 이용하면 된다. DSA 키의 생성과 마찬가지로 암호 알고리즘 지정 여부에 따라 암호화 된 키 또는 암호화되지 않은 키를 생성할 수 있다.

     # openssl genrsa -out rsa1024.key -des3 1024
     # openssl genrsa -out rsa1024.key 1024

 DH 파라미터는 다음과 같은 명령으로 생성한다.

     # openssl gendh -out dh1024.pem 1024

 * CSR 생성
 CSR(Certificate Signing Request)은 인증서를 만들기 위해서 CA(Certificate Autority)로 보내는 문서를 말한다. 실제로 이러한 서비스는 사설 인증 업체에서 수행하는 경우가 많기 때문에 개인적으로 CSR을 생성하고 이를 이용해서 인증서를 신청하는 작업이 조금 어려울 수도 있다. openssl에서 제공하는 다음 명령을 이용해서 CSR을 생성할 수 있다.

     # openssl req -new -config /etc/ssl/openssl.cnf -days 365 -key rsa1024.key > server.scr

 이 명령은 RSA 1024비트 키로 CSR을 만들며, 명령 수행 과정에서 openssl.cnf 파일을 필요로 한다. 실제로 이 명령을 수행할 때는 라이브러리와 함께 설치된 openssl.cnf 파일을 적절히 수정해서 사용하자. 이 명령은 앞 단계에서 만든 RSA 키를 이용하는 명령이며, DSA를 이용해서 생성할 수도 있다. 이 경우 위의 명령에서 rsa1024.key를 DSA 키 파일인 dsa1024.key로 대치하여 수행하면 된다.

 * CRT 생성
 이 과정은 앞에서 생성한 CSR을 이용해서 인증서를 만드는 과정이다. 실제로 이 과정은 CSR을 받은 공인 인증 기관을 통해서 수행하지만, 한정된 범위에서 암호화 통신 기능을 위해서 사용하는 정도라면 자신이 가지고 있는 키로 서명해서 이용할 수도 있다. 이러한 경우를 self-signing이라고 한다. 이 과정은 다음 명령을 통해서 수행한다.

     # openssl x509 -req -days 365 -in server.csr -signkey rsa1024.key -out server.crt

 만일 가상적으로 CA 서버를 만들고 싶다면, 이 과정을 응용해서 CA 서버를 구축하고 운영할 수도 있다.