어제까지 ARP Spoofing공격을 공부했다.

이 ARP Spoofing 공격이 가능한 이유는
인증과정에 대한 부재가 원인이다.

 인증과정을 거치면 속도가 느려진다. 그렇기 때문에 추가하지 못하고있다.
 (V3도 ARP Spoofing을 막아준다.)

막으려면
 arp -a 를 들어가서 ARP 테이블을 들어가보면 유형에 동적이라고 나와있다.
동적 -> 직접입력 안해도 되고 바로바로 인터넷을 쓸수 있다.
정적 -> 안전하다. 하지만 계속 업데이트를 직접 해줘야한다.
명령어
arp -s (옵션)
arp -s  (정적으로 바꿔줄 수 있다.)



(정적으로 바꾼 화면)



탐지 하는 법
1.서로다른 IP에 대해 중복되는 맥주소가 있을 때
2. 대량의 ARP 패킷이 탐지 될 때

Layer 3에 대한 이해
 - 어떻게 IP헤더만을 가지고 다른 네트워크와 통신이 가능한가?
 - 이더넷헤더 + IP 헤더
 
 - ICMP (Internet Control Message Protocol)
 - 네트워크상에 오류를 제어하기 위한 목적
 - 요즘엔 효율성은 별로 없는데 취약점들이 너무많아서
   ping정도만 사용된다.

ICMP - 오류상황 전달, 서버와의 시간 동기화 등
       많은 타입의 ICMP가 있다.


ICMP 공통헤더
 - type(1) : ICMP 패킷의 타입
 - code(1) : ICMP 패킷타입의 세부 정보
 - checksum(2)

ICMP 타입에 따른 헤더

- ping : Echo Request(8번) (ICMP의 한 종류다.)
서버입장에서 ping을 차단시키는 경우도 있다.
(공격으로 이용할 수 있기 때문이다.)

원래 용도는 상대와 통신이 잘되는지 테스트하는 용도다.

우리는 ping 패킷을 만들어볼 것이다.



(ICMP 패킷을 생성한 코드)




(출력해보면 헤더가 잘 만들어졌음을 확인 할 수 있다.)



type : 8번
 identifier(2)
 sequence number(2)    
 payload(?) - 정하기 나름이다. 여기 들어있는 데이터 그대로 응답이 온다.
      - 별 의미는 없다. ping의 크기를 조절하기 위한 것이다.



(ping 메세지 테스트를 위해 수정한 코드)





(ping을 보내고 응답을 받은 것을 확인할 수 있다.)




(cmd 창에서 ping 메세지를 사용해서 확인한 결과)



- 응답이 오는것을 확인 할 수 있다.

* IP Spoofing (L3에서 할 수 있는 공격)
 - IP값을 변조
 - 0ffset, IP 주소 등등

다음 주 부터는 IP에 관한 내용을 살펴보고
L4부터 취약점을 파악해보겠다.




어제 해본 공격으로 ARP Cache Poisoning이 있다.

오늘은 이 공격을 확장해 보겠다.


(확장된 공격)



이 코드는
보내는 사람의 소스 IP주소까지 변조하는 코드이다.
for문을 사용해서 192.168.3.0 부터 192.168.3.49 까지 한번에 쓰레기 값을 입력하는 것이다.


(공격받은 모습)




(공격받은 모습)


ARP 패킷을 하나씩만 보냈기 때문에 이 증상은 조금후 조금씩 회복된다.
하지만 while 문으로 꾸준하게 보내주게 되면
이 컴퓨터는 통신을 할 수 없는 상태에 빠지게 된다.

또 이것저것 바꿔보면서 실험해보았는데
이더넷 헤더의 맥주소는 바꿀 수 없었다.



(표시된 부분)


이더넷 헤더의 출발지 주소까지 변조하여 완전범죄를 꿈꾸었으나
이 헤더의 정보를 건드리면
컴퓨터에서 패킷이 전송되지 않는다.
우리는 ARP 데이터는 변조할 수 있다.

여기서 한가지 호기심이 더했다.

만약, 게이트웨이 ARP 테이블을 지금 위에서 한 확장된 ARP 캐쉬 Poisoning을 한다면
어떻게 될까??


(타겟의 IP주소를 게이트웨이 주소로 한 코드)


IP 주소 범우는 199까지 했다. 같은 네트워크에 200번 뒤도 있었지만
내 IP주소가 165번이었기 때문에 내 번호까지만 커버하는 IP 범위로 설정했다.



(공격 받은 후 상황)



게이트웨이로의 ping이 잘되다가 응답이 몇번 안된다.
하지만 이 현상은 오래 가지 않는다. (내가 while문으로 계속해서 돌리지 않았기 때문에)
내 컴퓨터는 게이트웨이의 주소를 ARP 과정을 거쳐 올바른 주소를 얻고
ping 메세지 아래 두개는 응답을 받은 것을 확인 할 수 있다.



(인터넷 멈춤 현상)



그리고 게이트웨이를 공격한 후에 인터넷 접속을 했더니 오래걸렸다.
이는 아까 위에서 본거 같이 ping메세지도 처음 두 메세지는 응답하지 못했지만
나중에 다시 ARP과정을 거쳐 메세지를 받았듯이
인터넷도 원래는 바로 켜졌는데, 시간이 조금 지난 후에 접속이 가능하였다.

여기서 만약 내가 while 문으로 계속 돌렸다면
네트워크는 동작하지 않았을 것이다.

또 위에서 확인해 보았듯이 이더넷 헤더는 변조가 안된다...
패킷을 덤프해서 확인해보니
내 맥주소가 나온다



(내 맥주소가 노출되는 상황)


만약 ARP 변조 공격을 당했다면, 혹은 탐지를 위해서는
패킷을 덤프해보면 확인 할 수 있다.
이더넷 헤더는 변조가 불가능하기 때문이다.


Layer 1 - 물리적인 전송을 담당한다.
            - 전기 신호
 ex) 허브에 스타토폴로지로 host들이 연결되있다.
 한 host에서 전기신호를 보내고 그 전기신호를 수신하여서 데이터를 주고받는다.

Layer 2 - 데이터 링크 계층
 
 - 터널링관련 프로토콜 : PPP, L2TP, ...
 - 라우팅이 필요하지 않는 계층
 - 라우팅없이도 통신이 가능하다.
- 이더넷, 토큰링방식
 토큰링 방식 (토큰을 가진사람만 보낼 수 있고, 그 토큰은 네트워크를 돌아다닌다., 충돌을 예방하는 방법이다.)
  - 단점 : 토큰이 돌아서 내 차례가 될때까지가 오래걸린다.
 이더넷 : Collison Domain (충돌을 이용한다.)
 이더넷 헤더 (고정 : 14바이트)
 -우리는 보통 이더넷을 사용한다.

* 각 계층별 주소
L4 - Port Number
L3 - IP Address
L2 - MAC 주소. (Media Access Control) : 하드웨어 주소
 - 16진수 6자리  (유일한 주소)

 L2까지 합하면 패킷의 구성은
 
   - 이더넷 + IP + TCP/UDP + 데이터

지금까지 IP, TCP/UDP, 데이터까지 보았다.
 
기존에 만들었던건 윈도우즈 소켓을 이용해 패킷을 덤프했던 것이다.
이 윈도우즈소켓으로는 L3까지밖에 확인이 안된다.
그래서
L2까지 보기위해 다른 방법을 사용해보겠다.

새로 만든 모듈을 이용했다.



(모듈로 설정한 get_device)





(실행화면)



어떤 장치로 들어오고 나가는 내용을 화면에 출력할지를 정해야한다.
화면에서 Microsoft 라고 나온것이 무선어댑터 지금 사용하는 것이다.

 실행하면 인터페이스를 선택하라고 나온다.
이 때 여기서 나오는 인터페이스란

제어판에서 네트워크 정보에갔을 때 나오는 목록이다.
이 목록은 컴퓨터의 네트워크 장치이다.
이제 이 장치로부터 패킷을 가져와 열어보겠다는 것이다.





(두개의 어댑터)



이 화면은 집마다 다르다.

여기서 전에 소켓통신했던것과 유사하게
핸들러(장치를 다룰) 를 선언했다. 소켓과 비슷한 역할이다.

그 후 전에 덤프 프로그램과 비슷하게 작성해준다.



(패킷을 덤프받아 출력하는 코드)



이렇게 하면 전에 사용했던 프로그램과 비슷한 모양의 코드가 된다.



(실행 화면)



출력을 해보면 데이터가 출력된다.
캡쳐된 내용을 보면 앞에 14바이트 빼고는 지금까지 봤던 정보들이다.

앞에 14바이트는 이더넷프레임이다.


['0xff', '0xff', '0xff', '0xff', '0xff', '0xff', '0x90', '0x9f', '0x33', '0xeb', '0x34', '0xa1', '0x8', '0x0']

분석해보면
1. 도착지 맥주소(6바이트) :  0xff', '0xff', '0xff', '0xff', '0xff', '0xff
2. 출발지 맥주소(6바이트) : '0x90', '0x9f', '0x33', '0xeb', '0x34', '0xa1'
3. 프로토콜 타입(2바이트) : '0x8', '0x0'
                                          - 0x0800 -> IPv4  , IP 프로토콜
                                            0x8600 -> IPv6
                                            0x0806 -> ARP 프로토콜

통신을 하려면 MAC주소를 알아야한다. 하지만 우리는 IP주소만 알고있다.
그렇기 때문에
ARP 가 필요하다.
ARP : Address Resolution Protocol

이제 전에 작성했던 프로그램에서 L2까지 나오는프로그램으로 확장시켜본다.



(확장 코드)




(출력 화면)


출력 화면을 보면 이제 L2의 프레임 단위로 패킷을 확인할 수 있는것을 볼 수 있다.

이제 내일부터
이 내용을 가지고 네트워크를 살펴보겠다.




어제 하던 통신프로그램을 보완해서

그럴싸한 통신을 하는 프로그램으로 업그레이드 했다.



(while 문을 이용해 메세지를 종료할 때 까지 통신하는 코드)


(메세지를 주고받는 모습)


(맨 아래줄에 보면 외부 IP주소와 포트번호가 나와있다.)



위 화면에서 외부주소 맨 아래를 보면 상대 IP주소와 포트번호가 나와있다.

코드에 적힌 대로 IP주소와 포트번호로 연결된것을 확인할 수 있다.

현재, 이 프로그램은 동기식 통신이다. 즉 한번 보내고 한번 받고 해야하는 방식이다.

여기서,

상대와 보통 채팅처럼 비동기식으로도 만들 수 있다.
내가 보내지않아도 상대가 마음껏 보낼 수 있는 상태로.

그러려면
Thread를 이용해야한다.

하나는 send만 하고 하나는 recv만 하도록 쓰레드를 열어서 실행해야한다.

하지만 현재 우리는 네트워크를 이해하는 차원에서
여기 까지 하겠다.

우리가 패킷을 분석하기 위해 만드는 프로그램이기 때문이다.

우리가 위에서 보낸 메세지나
네트워크에서 주고받는 데이터는
전달하기위해 여러가지가 붙어서 패킷을 만들어 전달된다.

이런 규칙들이 프로토콜인데
4계층의 TCP, UDP를 알아보겠다.

먼저 파이썬 코드로 어떻게 동작되는지 두 프로토콜의 차이점을 봐야한다.

동작 과정

* TCP server
 1. socket()
 2. bind()
 3. listen()
 4. accept()
 5., send(), recv()

* TCP Client
 1. socket()
 2. connect()    -> server의 accept와 연결된다.
 3. send(), recv()

* UDP server
 1. socket()
 2. bind()
 3. sendto() ,recvfrom()

* UDP server
 1. socket()
 2. sendto(), recvfrom()
-> send()를 못쓰는 이유는 send는 연결되어있는 곳으로 보내는건데
UDP는 연결된게 없으므로 sendto()로 그때그때 어디로 보내는지 적어줘야한다.

UDP의 특징
-> 연결과정 없이 바로 보내고 받는다.
-> 데이터가 갈수도있고 안갈 수도 있다. 그래서 TCP는 신회성이 있는 통신이다. 왜냐하면 안갔는지 갔는지 체크하기 때문이다. TCP는 체크하지만 UDP는 체크하지 않는다.
-> UDP는 신뢰할 수 없는 통신이다. ( 이말은 상대가 받았는지 체크할 수 없다는 뜻)
-> 인증 절차도 없다. 그냥 보내고 그냥 받는다.
보통 UDP를 사용하는 프로그램은 보통 인증과정이 없다.
UDP자체로는 인증을 할 수 없다.

그렇다면 왜 쓸까?
  위 말만 들으면 왜쓰는 걸까 싶지만 UDP의 큰 장점은 복잡하지않고 빨리빨리 보낼 수 있다는 것이다. 보내는 데이터가 크지 않다면 UDP가 적절하다. UDP는 에러체크등이 없고 그냥 보내기만 하는 것이다. 그래서 사실 예전에는 많이 쓰이지 않았다. 하지만 요즘 많이 쓰이고 있는것이 실시간 통신이 필요할 때 특히 UDP를 많이 쓴다. 빨리보낼 수 있고 에러 났다고 다시 보낼 필요가 없는 실시간 통신에 적합하다. 혹은 짧은 데이터 같은 경우가 적합하다.

- netstat -anp UDP
입력하면 UDP 포트 정보를 볼 수 있다. 여기에 상태 정보가 없다. 연결과정이 없기 때문이다.


(상태 부분이 없다.)


파이썬에서 UDP에 사용되는
recvfrom() 함수.
-> 받아온 내용보면 클라이언트 정보도 같이 출력된다.


(맨 아래 데이터와 데이터가 날라온 IP주소와 포트정보)


-> 튜블정보이므로 ip주소와 포트번호를 따로 입력받아서 출력해 봤다.


(튜플정보를 따로 받아와 출력한 모습)


TCP, UDP  -> Layer 4 (4계층)에 속한다.
-> 전송계층 (Transport Layer)

* PDU : Protocol Data Unit


-> packet : 패킷, 3계층에서 사용되는 단위
    근데 그냥 보내는 데이터 덩어리를 패킷이라고도 한다.


L4에서는 pdu가 segment 다.

-> 주소 체계 : Port번호

-> 각 계층별 사용하는 단위와 주소가 다르다.


패키징
-> Hello 데이터를 패키지화한다. 포장하듯이.
-> UDP 헤더를 붙인다. 데이터 앞에

헤더에 어떤 정보들이 들어가있는지 정해논다 -> 규약 Protocol

프로토콜에 맞게 데이터를 패키징해서 다음 계층으로 보낸다.



UDP 헤더에는 어떤 정보가 들어있을가?


UDP Header

 - dst Port 번호, src Port 번호
 - data length, checksum



-> 직접 확인해본다.

(먼저 패킷 데이터를 덤프받을 프로그램을 만들었다.)


socket.gethostname() 을 이용하면 컴퓨터의 이름을 얻어올 수 있다.

gethostbyname() 을 이용하면 이름을 가지고 호스트 정보를 가져올 수 있다. (IP정보)


-> 여기서 포트번호에 0을 쓴이유는 특정포트를 보기위해 하는게 아니라 특정 IP에 모든 포트들을 확인하는 것이다.


(여러번 실행한 경우)


뭔지 모를 데이터들이 오고가는 모습이다.


그러면 우리가 오늘 바로 작성한 UDP로 패킷을 보내고 이 패킷의 내용을 확인해보자.


(코드)


위 코드를 보면 info[0]에는 IP정보가 들어가므로 IP주소가 내꺼인 주소만 보겠다는 것이다.

이걸 안하면 이것저것 위에서 본거 처럼 구별하기가 힘들어서 그렇다.


이제 내가 UDP로 패킷을 보내고 이 프로그램으로 확인 해 볼 것이다.

보낼 데이터 : Hello


(출력 화면)


그림은 클릭하면 크게 보인다.


출력화면을 보면 끝에 Hello라고 적혀있는것이 보인다.

그리고 그 앞부분은 헤더의 내용이다.

UDP는 헤더에 많은 내용이 들어간다고 안햇는데 왜이렇게 길지???..


그건 UDP헤더만 붙는게 아니기 때문이다.


아래 계층으로 내려가면서 패키징되는데 점점 헤더가 붙어서 늘어나기 때문이다.


이제 앞으로 이 헤더 정보, 데이터 정보를 구별하여 읽는 법을 공부할 것이다.


다른 패킷을 분석하여 어떤 정보가 오고가는지 확인할 수 있게 될 것이다.







네트워크란


- 떨어져 있는 Host들간의 연결 혹은 연결되어있는 집단을 말한다.


네트워크의 기원


ALPANET -> 정부, 학교, 기업들간에 서로 데이터공유를 원활하게 하고자 해서 시작된 프로젝트이다.
              ->  이 때, router등 네트워크 장비들이 개발되었다.


그렇다면
네트워크에서 어떻게 데이터를 주고받을까?


-> 여러가지 프로토콜(규칙들)을 통해서 주고받는다.


네트워크에서 통신하는 모습을 오늘 확인해 볼 것이다.


네트워크의 구조부터 봐야한다.


* 네트워크 구조는 OSI 7 Layer (표준)로 이루어져있다.


크게 보면 아래와 같다.


1~4 계층
 - 하위 4계층이라고 한다.
 - 물리적인 전송 방법에 관해 역할을 담당한다.
 - 통신규약(프로토콜)
     -> TCP, UDP, IP, ARP, ETHERNET, ...  (데이터를 전달하는 방법들)


5~7 계층
 - 상위 3계층이라고 한다.
 - 논리적인 규칙에 따라 전송된다.
 - HTTP, FTP, Mail,... (프로토콜)
 - kakao talk, game.. (비표준프로토콜) -> 외부에 공개되지 않는 규약들

먼저, 하위 4계층을 이용해 문자를 전송해보자.
파이썬을 이용해 1~4계층으로 메세지를 직접 주고받는 통신을 해볼 것이다.
파이썬에서 통신을 하기 위해 필요한 내용들이다.


* 파이썬 소켓 프로그래밍

-> 우리가 하려는 것이 네트워크 상에서 데이터를 주고받는 모습을 확인하려는 것이다.
    그러기 위해서는 네트워크 프로그래밍을 해야한다. 네트워크 프로그래밍을 소켓 프로그래밍이라고도한다.
-> 네트워크 프로그래밍? : 통신을 위한 프로그래밍으로 소켓을 이용한다. 그래서 소켓프로그래밍이라고 하는 것이다.

 - socket? 소켓을 이해하기 위해 다음것들을 먼저 보는 것이 도움이 된다. 


* 입/출력 방식

 - 표준 입/출력 : input(), print()   (키보드, 모니터) (키보드, 모니터로 이루어지기 때문에 다른 식별자가 없다.)
 
- 파일 입/출력 : 파일 식별자가 필요하다. (어떤 파일을 열었는지 알아야하기 때문에 식별자가 필요하다.)

  - open ( 파일을 가져온다 )
   a, w, r    (추가, 쓰기 읽기전용)
   a -> 있으면 거기다 쓰고 없으면 새로 만든다.



(open() 을 이용해 파일입력해보는 코드)


(이렇게 바탕화면에 text 파일이 생긴다.)



핸들러 -> 파일 변수. 파일을 다룰수있는 변수


(핸들러 내용)


open 되있으면 파일이 열려있는것이다. 이 때 삭제를 누르면 열려있다고 삭제가 안된다. (열려있다는거 확인할 수 있다.)
close로 닫을 수 있다.


(Python 에서 close()를 하지 않은 경우)


(close() 를 해야 열려있던 파일이 종료된다.)


write -> 화면에는 입력한 문자의 길이가 표시된다.

* 파일을 다룰 때는 식별자를 주의해야한다.


(파일에 문자를 입력해본 결과(11이라고 적힌건 입력된 문자수를 출력한다.)


파일 접근 방법
ex) fd = open("파일 경로", "모드")

네트워크 입/출력도 파일 입/출력과 비슷하다고 생각하면 된다.
파일은 open으로 접근했다면 네트워크는 socket으로 접근한다고 생각하면 된다.

 - 네트워크 입/출력 : socket (파일의 디스크립터와 같은 맥락) 소켓은 식별자이다.
  ex) 서버는 여러개의 소켓꽂을 자리를 준비하고있다. 우리는 소켓을 들고가서 콕 꼽는다.
 -> 네트워크 프로그래밍을 소켓프로그래밍이라고도 한다.
 먼저 소켓을 생성해줘야한다. (파일입출력할때 파일을 생성한것처럼.)

1. socket 생성
 - socket module
 - 소켓 인자 : (1. 인터넷 타입, 2 통신 타입,  3. 프로토콜)

*인터넷 타입
 - AF_INET  (IPv4)
 - AF_INET6
 ...
 - AF_PACKET

* 통신 타입
 - 통신하는 타입
 - SOCKET_STREAM  (TCP)
 - SOCK_DGRAM     (UDP)
 - SOCK_RAW

* 프로토콜 -> defalt 값은 0이다. (안적어도 된다.)

출력해보면 볼수 있는데 fd가 중요하다. 소켓마다 번호가 다르다.
하나 더 만들면 번호가 다르게 생성된다.


(소켓 생성 모습)


* 네트워크 타입

 1. 서버-클라이언트 ( 1: N )
 2. Peer To Peer ( 1 : 1 )  (카톡, 채팅)
    -> 알고보면 이것도 서버-클라이언트다
    -> 누군가는 서버가 되야하고 클라이언트되야한다.
     -> 누군가는 반드시 전화를 먼저 걸어야한다. (전화를 예로)

윈도우즈 위에 여러개의 프로그램이 있다. 그래서 포트라는 개념이 필요하다.
Port 번호를 가지고 프로그램을 식별한다.
포트 번호는 ( 1~ 65535번 ) 까지 사용한다.
1~1024번 포트는 well-known 포트번호라고 한다. 조금 공식적인 포트 번호
1~10000번 까지는 비우는게 좋다. (요새는 통신하는게 많아져서)

* IPv4, IPv6 
 - IP Address : 네트워크상에서 호스트를 식별할 수 있는 식별자(식별번호)

ipv4
 - 4자리의 정수를 '.' 으로 구분해서 표시한다.
 - 각 정수의 범위는 (0~255) 이다.

ipv6
 - 6자리의 16진수 정수를 가지고 주소를 표현한다.
 - 0x00 ~ 0xff (255개)

커맨드창에 아래와 같이 입력하면
netstat -anp tcp
-> 현재 열려있는 포트들을 보여준다.



(netstat -anp tcp 명령어)


(포트 번호 50000번을 열어둔 상태)



(포트번호 50000번이 열려있는 것을 확인할 수 있다.)


이제 간단한 통신을 하는 프로그램을 만들어 볼것이다.

동작 방식은

* 서버 프로그램의 동작방식
1. 소켓을 생성한다.  -> socket()
2. 포트를 오픈한다.  ->bind()   : 튜플 형태로 하나의 인자. 튜플안에 뒤는 포트번호
3. 네트워크의 상태를 Listen 상태로 변경한다.  -> listen()  괄호 안에 연결 개수를 적기도한다.
4. 클라이언트의 연결 요청을 기다린다.     -> accept()  : 클라이언트 소켓을 리턴해준다.

* 클라이언트 프로그램의 동작방식
1. 소켓을 생성한다.   -> socket()
2. 서버에 연결한다.   -> connect()  : IP주소와 포트번호를 튜플형태로 적어준다.



(통신 프로그램 코드, connect()는 빠져있다.)


(서버 측 출력)


위 코드를 보면 서버 모드일 때 연결이 되면 연결되어있는 정보가 출력되는 것을 확인 할 수 있다.
리턴되는 값은 클라이언트 소켓 정보이다.

연결된 이후에는 send, recv 함수를 통해서 데이터를 송/수신 할 수 있다.

그러면 이제 메세지를 주고받겠다.



(문자열 abcde를 보내는 클라이언트 모드)



(에러가 났다.)

문자열을 그대로 보내면 파이썬은 처리를 못한다.
그래서 .encode()를 하면 파이썬이 적절하게 인코딩을 해서 보내주게 된다.


(서버에 입력이 들어온 모습)


내가 클라이언트 모드일때 abcde를 보냈고
내가 서버모드로 하고 옆사람이 클라이언트모드로 내게 Hello를 보낸 것을 확인 할 수있다.

출력 결과를보면 첫번째 꺼는
b' Hello라고 적혀있고
아래꺼는
Hello 라고 적혀있다.

첫번째꺼는 데이터 받은 것을 디코딩하지 않고 바로 출력한것이다.
보면 바이트 표시가 되있는 것을 볼 수 있다.

두번째 꺼는 .decode()를 한 모습이다.










+ Recent posts