오늘은 DNS Spoofing 공격을 해볼 것이다.

DNS Spoofing이란 DNS 서버를 조작하거나, 호스트에게 잘못된 DNS 정보를
보내어 잘못된 DNS 정보를 전달해 원하는 IP 주소로 이동하게 하는 것이다.

( 이 공격은 불법이므로 실제로 사용해서는 안됩니다.
 반드시 가상환경을 구축하거나 개인적으로 실험하세요 )
( 본인이 책임져야 됩니다.)

DNS 스푸핑을 쉽게 말하면
피해자가 naver.com 으로 입력했는데 다른 공격자가 원하는 사이트로 이동하는 것이다.

공격 환경
공격자 : 칼리리눅스 (120번 IP)
피해자 : 윈도우 ( 4 번 IP)

공격자인 내가 피해자가 naver로 접속할 때 학교 사이트로 넘어가게 할 것이다.

DNS 서버를 건드리는 것은 불법이므로
피해자의 DNS 캐쉬 테이블을 조작해서 공격을 성공시키겠다.

DNS 스푸핑을 하기 위해 칼리리눅스의 ettercap 툴을 이용할 것이다.
Ettercap 툴은 유명한 MTM 공격 툴로 우리의 공격목적에 알맞다.

먼저
Ettercap을 이용해서 dns spoofing을 조작할 것이므로
DNS 테이블을 조작할 것이다.

공격 계획은 이러하다.
1. 조작된 DNS 테이블을 만든다.
2. 피해자와 게이트웨이 중간에 들어간다. (MTM)
   (ARP Spoofing을 이용해서)
3. DNS Spoofing을 한다.
4. 게임 끝!



(dns 파일로 접속)





(naver 의 주소를 학교 IP주소로 변경한다.)



"*" 표시는 무엇이 들어와도 괜찮다는 이야기이다.
그래서 표현을 *.naver.* 으로 해준 것이다.

자 이제 테이블도 준비됬고 이 정보를 뿌리면 된다.
Ettercap을 열어준다.



(열어서 Unified sniffing 을 클릭한다.)








(네트워크 인터페이스를 선택)





(호스트 스캔한다.)





(스캔된 리스트 화면)






(타겟 설정)



타겟으로 게이트웨이와 피해자 호스트로 설정한다.

이 둘 중간에 들어가서 ARP Spoofing을 먼저 할 것이다.





(ARP 스푸핑)



ARP 스푸핑을 한다.
sniffer remote connections 을 선택한다. 이것은 포워딩도 같이 해주겠다는 것이다.

자 그럼 여기 까지 MTM 까지 왔다.

이제 DNS Spoofing을 해주면 된다.




(DNS Spoofing을 선택해준다.)



이제 피해자는 인터넷 주소창에 naver.com 을 입력하면
DNS 캐쉬 테이블에 적혀있는 아이피 주소로 바로 이동할 것이다.




(DNS 스푸핑된 모습)



naver.com에 입력했지만
naver가 아닌 다른 사이트가 열린다.

이로써 DNS Spoofing 공격을 성공했다.





칼리 리눅스를 이용해 ARP Spoofing과 Sniffing 공격을 해보겠다.
물론,
실제 서버, 실 사용자에게 공격을 하는 것은 불법이다.
즉, 실제로 사용한다면 본인이 그에 대한 책임을 져야한다.

공격할 환경을 구축해야한다.
VM을 이용해
칼리리눅스(서버) - IP 주소 100번
칼리리눅스(공격자) - IP 주소 120번
윈도우(피해자) - IP주소 4번

상황은 피해자가 서버에 접속을 하는 상황이다. (TELNET 접속)
공격자는 이 때 서버의 계정과 비밀번호를 얻더내는 것이 목표이다.

먼저
피해자가 서버와 통신을 할 것이고 이 때 TELNET에 계정과 비밀번호를 전송할 것이다.

TELNET은 통신이 암호화되어있지 않기 때문에
패킷의 내용을 가져올 수 있다면 즉 스니핑을 할 수 있다면
바로 게임 끝이다.!

이제 관건은 어떻게 스니핑을 할 것인지가 관건이다.
스니핑을 하기 위해
ARP 스푸핑을 이용할 것이다.
네트워크 해킹의 일종으로 ARP Poisoning이라고도 한다.

그래서 피해자와 서버 사이에서 중간자에 들어갈 것이고
이 상황에서 스니핑을 할것이다.
그 후 계정 탈취

계획을 정리하겠다.

1. ARP 스푸핑
2. 스니핑
3. 끝

칼리리눅스의 arpspoofing 툴을 이용해 먼저 스푸핑을 시작한다.




(arpspoofing 명령)





(arp reply 패킷이 전송되는 모습)




피해자의 입장에서 보면


(공격 받기 전 ARP 테이블)





(스푸핑 당한 상황)



이 상황에서는 피해자의 패킷들이 전부 나(공격자)에게 전송되므로
피해자는 현재 인터넷 통신이 되지 않는다.





(인터넷이 끊긴 상황)



이대로 놔두면 피해자는 이상하게 여길 것이고 공격받고 있다는 것을
눈치 챌것이다.

가로챈 패킷을 다시 정상상태로 포워딩을 시켜줘야
피해자가 인터넷 통신이 되고 공격받는 사실을 눈치 채지 못할 것이다.
또 다시 포워딩을 해주면서 나는 피해자와 서버 사이에 들어가게 되는 것이다.

피해자 < - > 나  < - > 서버





(포워딩 해주는 모습)



자 이제 중간까지 들어갔다.

패킷 스니핑을 하면 된다.

우리는 와이어샤크를 이용할 것이다.
와이어 샤크를 이용해 패킷을 확인해 볼 것이다.




(와이어샤크를 캡쳐 중인 모습)



자 이제 와이어 샤크로 감지하고 있고
피해자가 접속하길 기다리면된다.

피해자가 접속을 한다.




(텔넷 서버에 접속하는 모습(피해자 화면))





(접속한 상황(피해자 화면))



이 때 바로
패킷을 분석해보니
TELNET프로토콜을 확인할 수 있었다.
전체적인 내용을 확인하기 위해 Follow TCP Stream을 클릭해준다.




(Follow TCP Stream)





(에러 화면)



에러 화면이다...

이 문제로 실제
밤을 새웠다..
구글도 뒤져보고 실제로 이 문제로 최근 바로 16년 12월에 버그로 보고가 된 부분이었다.
그래서 와이어샤크는 2.2.2 버젼에 이 버그를 패치해서 올려놨는데

확인해보니 내 버젼은 2.2.0 ...
업그레이드 해주었다.
그 후 잘되는 모습이다.





(계정, 패스워드 탈취)



이렇게 TELNET 계정과 패스워드를 획득할 수 있었다.
공격 성공.

!!!



오늘은 전송계층인 L4의 취약점을 분석해 보겠다.

Layer 4에는 TCP와 UDP가 있다.
오늘은 L4 중에서 UDP의 취약점을 분석해볼 것이다.

* UDP헤더를 이용한 공격 기법
1) 스캐닝
2) UDP Flooding

1. 스캐닝
- 타겟의 정보를 수집하는 방법
- 어떠한 서비스가 열려있는지 ,포트가 열려있는지 확인하는 방법
- 내 컴퓨터에서 netstat 치면 나오는 정보를 알아내는 방법이다.
- 다른 host에서 netstat의 정보를 확인하려는 방법이다.
- 그 중 UDP 스캐닝 기법이 있다. (UDP 통신 방법을 이용해서)
- 해당 포트가 열려있을 때와 닫혀있을 때가 돌아오는 반응이 다르다.
 -> 이걸 이용해서 스캐닝을 하는 방법이다.



(확인하려는 정보)



위의 화면을 확인해보려는 것이다. cmd 창에서 netstat 명령어로 확인 할 수 있다.
하지만 상대 host의 정보는 쉽게 확인할 수 없으므로
스캐닝 이란 작업을 한다.



(UDP 스캔 코드)



위는 윈도우 소켓을 이용해서 UDP 패킷을 만들고 전송하는 코드이다.
여기서 원하는 포트번호 50000번을 지정해서 보냈다.
(옆 자리 사람이 서버를 열어둔 상태이다.)



(아무 응답이 없다.)



-> 이렇게 아무 응답이 없다면 포트가 열려있다는 것이다.
그렇다면 닫혀있다면 어떤 반응일까?



(포트가 닫혀있을 때)



포트가 닫혀있을 때는 위와 같이 ICMP 에러 패킷이 전송된다.
우리는 이로써 포트가 닫혀있는 걸 알 수 있다.

정리하면
포트가 열려있으면 -> 아무 응답 없다.
포트가 닫혀있으면 -> ICMP 에러 패킷이 온다.

- 프로토콜에 따라 다르겠지만, 데이터를 서비스에 맞게 설정한 경우 응답이 올 수 있다.
 -> 포트가 닫혀있는 경우 icmp error 메세지로 응답이 온다.
 - destination unreachable 로 응답이 온다.
 - 확인하고자 하는 host의 방화벽이 켜져있으면 icmp응답이 오지 않는다.
 - 그래서 방화벽을 끄고 실험했다. 여기서 알 수 있는 것은
   방화벽으로 UDP 스캐닝을 막을 수 있다는 것이다.
 - 우리는 패킷 수로 체크를 해서 트래픽 수가 많아지면 정확도가 떨어졌다.
 - 스캔 툴 : nmap (유명한 대표적인 스캐닝 도구)


* 주의. 스캐닝 아무대나 하면 안된다. (엄연한 불법이다.)

2. UDP헤더를 이용한 DDos 공격 기법

- UDP Flooding 기법 : 대량의 UDP 패킷을 발생시키는 공격기법
  -> 공격의 목적이 특정 타겟이 아니라 타겟 네트워크의 대역폭을 대상으로 하는 공격기법이다.
  -> 네트워크 전체를 마비시키는 목적이다.
  -> 포트가 닫혀있으면 에러메세지를 보낸다.
  -> 출발지 ip 주소를 스푸핑한다. (해당 네트워크 안에 있는 IP주소로)
  -> 해당 네트워크 내에서 에러메세지가 돌아다닌다.
  -> 이 양이 대량이면 그 해당 네트워크의 대역폭을 감소시킬 수 있다.
  -> 해결방법은 ICMP 패킷을 안쓰는 것이다. (그렇기 때문에 요즘 ICMP를 안쓴다.)
     ( 아까 확인했듯이 방화벽이 켜져있으면 ICMP 에러메세지를 만들지 안는다.)
  -> 윈도우 소켓으로는 IP 스푸핑이 안된다.
  -> L2까지 보내던 방법으로 보내서 확인 할 수 있다. (sleep()을 주어서 조금 지연시켜야한다.)
     그렇지 않으면 와이어샤크가 뻗어버린다.

확인해 보겠다.




(UDP Flooding 코드)



코드는 위와 같다.

내가 UDP Flooding 공격을 받은 상황은 다음과 같다.
위 코드의 화면은 아니다.





다른 사람이 나의 컴퓨터를 이용해 167번 컴퓨터를 공격하고 있는 모습이다.
공격자는 공격 대상 host의 ip주소로 출발 ip를 스푸핑 해서
나에게 UDP 패킷을 보낸다.
그리고 나는 닫혀있으므로 공격 대상(피해자)에게 대량의 ICMP 패킷을 보내는 것이다.

위 화면은 sleep 을 걸어두어서 확인 할 수 있는 정도의 양이 나왔다.
실제로 sleep을 안걸었을 때 와이어샤크가 동작을 멈추었다.




어제 만들던 코드는 완성됬다.
(내가 만든건아니지만 결국)

어제 만들던 원리 그대로 이어 붙였다.



(패킷 필터 결과화면)




위 화면은 python sniffer.py -p arp -s 192.168.3.18 -d 192.168.167 로 실행시킨 것이다.

옵션에 맞게 정상적으로 출력되는 것을 확인 할 수 있다.

이제 스니퍼 프로그램을 만드는데 있어서
패킷덤프 프로그램도 우리가 보기 편하게 만들었고,
ARP spoofing 할 수 있는 프로그램도 만들었다.

상대방의 패킷을 보려면, 스니핑을 하려면 지금까지 만든거로는 조금 부족하다.
왜냐하면
상대가 스니핑 당하는 것을 알아차리기 때문이다.

우리가 포워딩을 안해주기 때문에 상대 호스트는 나에게 패킷을 전송하고 있다.
그리고 그 결과를 못받고 있는 상황이다.
그러기 때문에 통신이 끊기게 되고 피해자는 알아차리게 된다.
더군다나 통신이 안되기 때문에 스니핑 할 수 있는 자료도 RST 패킷 , SYN 패킷등 이런거 밖에 볼수 없다.



(SYN, RST 패킷밖에 안보인다.)



 위 결과 화면은 상대 호스트 ARP 테이블에서 게이트웨이를 스푸핑한 상태이다.
상대 호스트는 게이트웨이가 내 컴퓨터의 주소인 줄 알고 나에게 보내는 상황이다.

하지만 그에 대한 응답이 안오고 있다. ( 이유는 내가 지금 포워딩을 안해주고 있기 때문에 )


이렇듯, 포워딩을 해줘야 완벽한 스니핑을 할 수 있게 된다.

포워딩이란 상대 호스트의 패킷이 원래 가던 길로 내가 다시 보내줘야된다는 뜻이다.
정확한 뜻은 패킷이 목적지 까지 도착하도록 올바른 길로 보내는 것을 포워딩이라고 한다.

그렇기 때문에 들어온 패킷을 원래 보내려던 목적지 호스트의 맥주소를 참조해서 패킷을 새로 만들고 보내 줘야한다.
목적지 호스트의 맥주소를 알기 위해서 우리는 ARP 테이블을 사용할 것이다.
(우리의 ARP 테이블에 목적지가 있다고 가정했다.)

우리는 이 기능들을 subprocess 모듈을 이용해서 구현할 수 있다.



(subprocess)



subprocess에서 arp -a 명령어로 ARP 테이블을 확인 할 수 있는 화면이다.



(맥 주소를 구해내는 코드)



우리는 위의 방법으로 arp 테이블에서 목적지 호스트의 맥주소를 참조할 것이다.



(포워딩 부분 코드)



위의 코드로 생성을 했다.

하지만 여기서 문제가 있었다.
상대호스트에게 ARP 스푸핑을 하는데
내 컴퓨터의 ARP 테이블에도 영향이 간다는 것이다..

그래서 완벽하게 스니핑 프로그램을 완성하지 못했다.

지금 마주한 문제를 곰곰히 생각해봐야겠다.




스니프( Sniff )
Packet sniffing
 - 남의 패킷을 가로채는 공격기법들
 - ARP Spoofing (ARP를 속여서 가로채는 것)
 = ARP Cache Poisoning 공격 기법

 - MITM ( Man In The Middle ) 중간자 공격

우리가 쓰고 있던 덤프 프로그램을 스니퍼 프로그램으로 만들어 보겠다.

우선 필요한건
1. 장치명을 매번 입력받는것
2. 원하는 패킷만을 보길 원한다.
  -> 소스코드를 수정하면서 확인했었다.
이다.

먼저, 실행인자를 이용해볼 것이다.
예를 들어



(ipconfig /all 옵션)



ipconfig 뒤에 붙는 옵션처럼
원하는 필터링의 옵션을 추가할 것이다.

스니핑 프로그램을 제대로 만들기 위해
먼저 패킷덤프 프로그램을 쓸모있게 만드는게 필요하다.

필요한 인자를 받아서 옵션을 만들것이다.
-> sys, optionparse  모듈이 파이썬에 이미 있다.
우리는 이것을 이용할 것이다.




(파이썬3의 optparse 모듈)



파이썬3의 optparse 모듈

인자를 받는 모듈을 이용해서
옵션을 5개 추가해 보았다.

옵션처리 방식(닉스계열 (유닉스, 리눅스))
#> python sniffer.py -t 192.168.3.88
       -p ARP    
       -s  (출발지 IP)
       -d (도착지 IP)

#> python sniffer.py --protocol ARP
       --target          (긴옵션)

* 필터링 룰을 넣어줄 것이다.
option
 -s : 출발지 ip 주소
 -d : 도착지 ip 주소
 -a : 출발지와 도착지 상관없이 필터링
 -p : 확인하고자 하는 프로토콜 타입
 -m : 동작모드(덤프/스니프)



(옵션 설정 코드)



(옵션이 입력된 것을 확인했다.)



-h 의 도움말 옵션까지 추가해 그럴싸한
프로그램으로 만들생각이다.



(usage를 추가하고 -h 인자를 추가한 코드)



(python test.py -h 를 입력하면 옵션이 나온다.)



우선
ARP 만 출력하는 모습을 보이겠다.

옵션 -p ARP 를 입력하면
ARP 만 나오게 하는 코드이다.



(옵션을 추가해서 조건을 걸은 코드)




(실행화면)



실행화면을 보면
ARP 패킷만 덤프되는  것을 확인 할 수 있었다.







어제 해본 공격으로 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 변조 공격을 당했다면, 혹은 탐지를 위해서는
패킷을 덤프해보면 확인 할 수 있다.
이더넷 헤더는 변조가 불가능하기 때문이다.


오늘은 지금까지 분석해왔던 패킷을 직접 만들어볼 것이다.

오늘은 L2 계층의 패킷을 만들어볼 것이다.

대표적인 ARP 패킷을 만들것이다.

ARP패킷에 들어가는 내용은
이더넷 헤더(14바이트), ARP 헤더(28바이트) -> 총 42바이트가 들어간다.

먼저
패킷을 만드는 코드를 짜야된다.


(패킷 생성 코드)



가운데 주석 표시해 둔 곳에
내용에 맞는 패킷을 작성하면 된다.


(패킷 내용을 작성한 코드)



(실행해서 덤프받은 화면)



위 화면은 ARP 패킷을 전송한 것을 덤프받아서 확인한 결과이다.

아래 바로 ARP 응답 패킷또한 확인 할 수 있다.
ARP 응답은 Operation이 2이다.


ARP 패킷을 만들어서 전송하면서 발견한 취약점.

* ARP 패킷을 받으면 그 받은 host는 그 내용을 무조건 신뢰하며
그 데이터에 따라 자기 캐쉬 테이블을 업데이트한다.

우리가 패킷 내용을 작성해서 보냈지만,
솔직하게 데이터를 쓰지 않을 수도 있는것 아닐까?
그렇게 되면 상대 host의 ARP 테이블은 어떻게 될까?



(나의 맥주소를 77:77:77:77:77:77 이라고 속인 코드)



이렇게 ARP 패킷을 스푸핑했다.
스푸핑 -> 속이다.

옆 사람이 내 컴퓨터로 ARP 패킷을 조작해서 보낸 결과는 다음과 같다.



(아래에 192.168.3.239 주소 맥주소)



192.168.3.239의 맥주소를 보면 이상하다.
aa-bb-cc-dd-ee-ff 라니?...

게다가 하면서 더 요상한걸 발견했다.

ARP 패킷을 보내는데
IP주소도 변조해서 보낸다면?...



(엉망이된 나의 ARP 테이블)



위 화면을 보면 요상한 맥주소들이 등록되어있다.
이 경우 아주 착하게도 맥주소가 잘못된것을 바로 알기 쉽지만
보통 일반적인쓰레기맥주소를 입력한다면 구별하기 힘들것이다.

또 이로 인해 네트워크 통신에 문제가 생길 수 있다.

바로 이렇게 ARP 테이블을 망쳐놓는다 해서
이 공격이름은 ARP Cache Poisoning 이라고 한다.





저번주까지 UDP 패킷분석, 그리고 IP 패킷을 분석하였다.

오늘은 TCP 패킷을 분석해보았다.

TCP는 UDP와 달리 조금 까다로운 부분이 많았다.

포함관계를 다시 정리하자면

IP 패킷안에 TCP 혹은 UDP가 들어가는 것이다.

저번주 까지 했던 패킷을 덤프받는 프로그램에서
이제 IP헤더를 분석할 수 있었고
그에 따라
소스 IP주소와 목적지 IP주소를 파악할 수 있었으므로

조금 더 정교한 필터링을 할 수 있게 되었다.



(조건 문에 IP주소로 필터링 하는 코드)


또, IP 헤더에서 Protocol 타입을 알아낼 수 있으므로
TCP, UDP, ICMP를 구분하기로 했다.



(Protocol 필드로 구분하는 모습)


위 코드를 보면 이제 TCP 헤더를 분석해볼 것이므로
처음에 list를 이용해 16진수로 표현해보았다.




(오른쪽화면은 저번주에 만들었던 TCP 메세지전송 프로그램.)


위 화면을 보면 TCP 데이터가 나오는 것을 확인 할 수 있다.

UDP와 다른점은 UDP는 메세지 하나 보내면 끝이었는데
TCP는 그 전에 많은 패킷들이 왔다 갔다 한다.
자세한 특징은 다음에 더 알아보겠다.
우선,
TCP 패킷의 헤더를 분석하는 것이 먼저다.

우리가 덤프받은 TCP 패킷의 헤더
['0xc3', '0x29', '0xea', '0x60', '0x2a', '0x4', '0x15',
'0x1a', '0x0', '0x0', '0x0', '0x0', '0x80', '0x2', '0x20', '0x0',
 '0xd9', '0x88', '0x0', '0x0', '0x2', '0x4', '0x5', '0xb4', '0x1', '0x3',
'0x3', '0x2', '0x1', '0x1', '0x4', '0x2']

내용은 이렇다.
1. 출발지 포트번호(2바이트) : '0xc3', '0x29' = 0xc329 = 49961
2. 도착지 포트번호(2바이트) : '0xea', '0x60' = 0xea60 = 60000
3.  Sequence Number(4바이트) : '0x2a', '0x4', '0x15', '0x1a'
- 운영체제에 의하여 랜덤하게 시작된다. Session을 표현하는 고유값이다.
4. Acknowledge Number(4바이트) : '0x0', '0x0', '0x0', '0x0'
*'0x80' -> 8과 0으로 본다.
5. TCP 헤더의 길이 (4비트) -> 8 (x4) = 32바이트
6.  예약된 영역 : 0 (사용하지 않는 필드이다.)
7. Flag : '0x2' = 2 (여기서는 SYN패킷, 동기화요청)
Flag 종류
1 : FIN
2: SYN
4: RST(reset)
8: PSH(push)
16: ACK(응답)
32: URG(urgent) 
(만약 동시에 설정된다면, 예를들어 SYN과 ACK가 같이 설정되면 두개의 합이된다.)
(ex. SYN-ACK 은 18번이 된다.)

8. window size (2바이트) : '0x20', '0x0'
9. TCP의 Checksum(2바이트) : '0xd9', '0x88'
10. Urgent Pointer(2바이트) : '0x0', '0x0'
---- 여기까지 딱 20바이트다.-----------------

그 이후로 옵션이 더 붙어서 패킷이 보내질 수도 있다.

자. 그러면 이제 이 내용을 바탕으로 헤더내용을
분석하여 출력해보겠다.


(TCP 패킷 분석코드)


(TCP 헤더 분석 내용)



여기서
Header 길이를 표현하려면 아까 한 바이트를 4비트로 나누어야한다.
이제 그 작업을 시작하겠다.

0x45를 4와 5로 분리하는 것이다.
먼저 0x45를 2진수로 변환한다. (bin()함수 사용)

그 후 슬라이스로 4개씩 자르고 int로 형변환 해준다.
아래와 같다.


(분리된 모습)



이걸 이용해
IP 헤더의 버젼과 헤더길이를 나누고
TCP의 길이를 구하겠다.

또 IP헤더에서 헤더길이를 정확하게 구할 수 있고 그러면
UDP,TCP 패킷이 어디서부터 시작하는지 또한 정확하게 표현할 수 있다.

(지금까지는 그냥 IP헤더가 20바이트라고 가정하고 했었다.)




(헤더 길이를 분리하는 코드)



(UDP의 시작 슬라이스와 TCP 헤더 길이, 또 flag를 표현해 수정해주었다.)



flag를 dictionary로 정의하여 키값을 대입하면 그에 대응하는 값이 출력된다.



(결과 화면)



지금 까지 TCP패킷을 분석해보았고

IP헤더도 더 정밀하게 분석하였다.

내일은 현재 진행중인 패킷 분석 프로그램을 조금 더 수정하고
본격적으로 패킷을 확인하고 변조해 보겠다.









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

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



(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