[NS]Ep.1 Passive attack 실습

GLICO·2024년 9월 5일

NS

목록 보기
1/6

해당 게시물에서 이용하는 실습환경은 다음과 같이 구축되었습니다.

scapy

What is scapy

Manipulate packets

Scapy is a powerful interactive packet manipulation library written in Python. Scapy is able to forge or decode packets of a wide number of protocols, send them on the wire, capture them, match requests and replies, and much more.

Installation

https://scapy.net/
https://github.com/secdev/scapy

sudo apt install python3-scapy

Backgrounds

해당 섹터(Backgrounds)는 참고용입니다.

https://github.com/secdev/scapy/blob/master/doc/notebooks/Scapy%20in%2015%20minutes.ipynb

from scapy.all import *

First steps

Scapy에서, 각 네트워크 레이어는 Python 클래스이다.
'/' 연산자는 레이어들을 하나로 묶어주는 역할을 한다.
IP의 최상단에 TCP 세그먼트를 설정하고 packet이라는 변수에 할당한 이후, 해당 세그먼트를 Ethernet의 위에 쌓아보자.

packet = IP() / TCP()
Ether() / packet

참고
Ethernet : Layer 2 Protocol
IP : Layer 3 Protocol
TCP : Layer 4 Protocol

마지막 출력은 패킷 요약(Packet summary)에 해당한다.
Scapy는 Ethernet뿐만 아니라 IP 프로토콜 필드를 자동으로 채운다.

프로토콜 필드는 ls()함수를 사용하여 리스트될 수 있다.

특정 IP 목적지에 대한 새로운 패킷을 만들어보자. 각 프로토콜 필드는 특정될 수 있다. 위 ls()의 결과를 통해 보이듯이 우리가 조작할 필드는 dst이다.

summary()함수를 통하여 확인해보자.

이전 예시와 크게 다를 것은 없지만, Scapy는 특별한 트릭을 수행하기 위해서 특정 목적지를 사용했다.

DNS resolution, routing table, ARP resolution과 같은 내장 메커니즘을 사용하면서, Scapy는 자동으로 패킷을 전송하기 위해 필수적인 필드들을 세팅한다.

print(p.dst)  # first layer that has an src field, here Ether
print(p[IP].src)  # explicitly access the src field of the IP layer

# sprintf() is a useful method to display fields
print(p.sprintf("%Ether.src% > %Ether.dst%\n%IP.src% > %IP.dst%"))

Scapy는 내재적인(Implicit) 패킷을 가지고 있다. 예를 들어, traceroute를 흉내내기 위해서 TTL 필드의 값을 1부터 5까지 만드는데 유용하다.

Sending and Receiving

패킷을 생성하는 방법을 배웠다. 다음 단계는 패킷을 네트워크를 통해서 전송하는 것이다.

sr1()함수는 패킷을 전송하고 그에 해당하는 응답을 반환한다.
srp1()함수는 같은 역할을 하지만, Ethernet과 같은 2계층 패킷들을 위한 함수이다.
오직 패킷을 전송하는데에만 관심있다면, send()함수는 너의 친구다 ㅋㅋ.

한 예시로써, 우리는 www.example.com IPv4주소를 얻기 위해서 DNS 프로토콜을 사용할 수 있다.

또 다른 방법으로는 sr()함수를 사용하는 것이다.
srp1()과 같이, sr1()함수는 2계층 패킷을 위해 사용될 수 있다.

함수명2계층 패킷 지원3계층 패킷 지원
srp1()OX
sp1()OO

  • r은 result의 목록이다. (전송 패킷과 응답 결과의 tuple)
  • u는 unanswered 패킷들의 목록이다.

# Access the first tuple
print(r[0][0].summary())  # the packet sent
print(r[0][1].summary())  # the answer received

# Access the ICMP layer. Scapy received a time-exceeded error message
r[0][1][ICMP]

네트워크를 Sniffing하는 것은 패킷을 주고 받는 것 만큼 직관적이다.
sniff()함수는 조작될 수 있는 Scapy 패킷들의 목록을 반환한다.

sniff()는 많은 인자들을 가진다. prn은 수신 패킷에서 불려질 수 있는 함수명을 받는다.
lambda 키워드를 사용하여, tshark 명령어의 행동을 흉내낸는데에 사용될 수 있다.

Scapy는 패킷의 송수신을 위해 OS sockets을 사용할 수 있다.
StreamSocket을 이용하여 UDP 소켓을 할당하는 예제를 보자.

import socket

sck = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # create an UDP socket
sck.connect(("8.8.8.8", 53))  # connect to 8.8.8.8 on 53/UDP

# Create the StreamSocket and gives the class used to decode the answer
ssck = StreamSocket(sck)
ssck.basecls = DNS

# Send the DNS query
ssck.sr1(DNS(rd=1, qd=DNSQR(qname="www.example.com")))

Visualization

matplotlib를 이용하여 시각화 예시를 따라해보자.

srloop()함수를 사용하여, 우리는 100개의 ICMP 패킷을 8.8.8.8과 8.8.4.4에 보낼 수 있다.

raw() 생성자는 유선으로 전송되는 패킷의 바이트를 "구축"하는 데 사용할 수 있다.

  • 패킷에 대한 summary 출력

  • "hexdump" 패킷 바이트

  • 각 필드에 대한 값과 함께, 계층 별 패킷 덤프

Scapy는 traceroute()함수를 가지고 있다.

Task 1

Scapy의 traceroute()함수를 사용하지 않고, "google.com"의 10번째 host ip를 알아내는 Scapy CLI 커맨드를 작성하시오.

traceroutepingICMP(Internet Control Message Protocol) 프로토콜을 활용하는 유틸리티이다.

참고
ICMP는 말 그대로 인터넷 제어 메시지 프로토콜이다.
IP(Internet Protocol)를 위한 보조 프로토콜인데..
네트워크 제어를 위한 각종 메시지가 규정되어 있다.
정말 나이브(Naive)하게 생각해보면, traceroute와 ping이 ICMP를 이용하여 time-exceeded, echo-request등의 메시지를 보냄을 알 수 있다.

traceroute는 해당 장비까지 가는 경로를 추적하는 프로그램인데,
모든 라우터는 패킷을 수신하면, TTL(Time To Live, 패킷의 유효 시간)을 1 감소시키고 전달한다. 즉, hop을 거칠 때 마다 TTL의 값을 1 감소시킨다. 만약 TTL이 0이 되면 패킷을 버리고, 대신 time-exceeded 메시지를 송신측에게 돌려준다. 이런 특성을 이용해서 최종 목적지까지 가는 경로를 모두 확인할 수 있다.


IP 패킷 생성 시에, ttl의 값을 10으로 설정해주었고, sr1함수를 사용하여 응답의 결과를 확인해보았다.

192.168.64.12의 로컬의 IP 주소이다.
해당 출력결과는 p3 패킷의 응답의 결과이기 때문에,
src는 10번째 hop에 해당하는 host/router의 IP 주소가 된다.


살짝 더 패킷을 뜯어보면,
google.com을 목적지로 패킷(p3)을 생성했다. (사진 최상단의 코드)

p3 = IP(dst="google.com", ttl=10) / ICMP() / "20201750"

DNS를 활용하여 해당 도메인(google.com)은 172.217.26.238로 변환되었고,
원래 송신지(src) 192.168.64.12에서
원래 목적지(dst) 172.217.26.238로의 필드가 채워진 것을
###[ IP in ICMP ] ### 섹션에서 볼 수 있다.

데이터통신을 공부하면 알겠지만,
실제 노드와 노드간의 통신을 담당하는 것은 2계층(Data-Link Layer)의 역할이다.
프레임(2계층 패킷의 단위)의 2개의 주소(src, dst)는 링크에서 다른 링크로 이동할 때마다 변경되고
데이터그램(3계층 패킷의 단위)의 2개의 주소(src, dst)는 데이터 전송 도중에 변경되지 않음에 유의해라.

프레이밍(Framing)과 캡슐화(Encapsulation)는 다음 링크를 참고해라.
DCN Ep 7



ttl의 값을 1씩 증가시키며 패킷을 새롭게 설정하고, sr1함수로 각 변수에(ttl1, ttl2, ttl3...) 응답 값을 할당하고 있다.


해당 변수들의 값을 확인해보면, 로컬에서 google.com으로의 5번째 hop까지의 경로를 알 수 있다.


Scapy에서 제공하는 traceroute와 결과가 같다. (프하하..)


Task 2

http://academy.kitri.re.kr 사이트의 입력 id, pw를 sniff하여 출력하는 CLI 커맨드 작성해라.

from scapy.layers.http import * # For use HTTP
from scapy.all import *			# For use Raw

packets = sniff(iface = "enp0s1", lfilter = lambda f : f.haslayer(HTTPRequest), prn = lambda x : x[HTTPRequest][Raw].show())

lfilterprn을 이용하여 커맨드를 작성하였다.
lfilter의 경우 lambda 식이 true인 경우 해당 필터가 적용된다.
HTTP 메소드를 보내기 위해서 [HTTPRequest]를 이용해주었다.
[Raw]를 사용하면 payload(body)만 받아서 볼 수 있다.

로그인을 위해 작성한 ID & PW가 sniff된 것을 확인할 수 있다.

profile
Its me Glico

0개의 댓글